Say I'm trying to translate the below Java classes to GNU Smalltalk:
public abstract class Account {
protected String number;
protected Customer customer;
protected double balance;
public abstract void accrue(double rate);
public double balance() {
return balance;
}
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
balance -= amount;
}
public String toString() {
return number + ":" + customer + ":" + balance;
}
}
public class SavingsAccount extends Account {
private double interest = 0;
public SavingsAccount(String number, Customer customer, double balance) {
this.number = number;
this.customer = customer;
this.balance = balance;
}
public void accrue(double rate) {
balance += balance * rate;
interest += interest * rate;
}
}
I'm struggling to understand how I can write methods/constructors that take multiple parameters. Here's what I've got so far:
Object subclass: Account [
|number customer balance|
balance [
^balance
]
deposit: amount [
balance := balance + amount
]
withdraw: amount [
balance := balance - amount
]
asString [
^number asString, ':', customer asString, ':', balance asString
]
]
Account subclass: SavingsAccount [
|interest|
SavingsAccount class [
new [ "add some sort of support for multiple arguments?"
"call init"
]
]
init [ "add some sort of support for multiple arguments?"
interest := 0.
balance := accountBalance.
customer := accountCustomer.
number := accountNumber
]
accrue: rate [
balance := balance + (balance * rate).
interest := interest + (interest * rate)
]
]
A few questions:
How can I make Account an abstract class in Smalltalk?
Am I correct in assuming all the Account instance variables are just accessible by name in the SavingsAccount Class?
How can I implement something that mimics the multiple parameter constructor in the Java SavingsAccount Class?
You shouldn't bother with some kind of "making class abstract" :). But the closest solution to your question is
abstractMethod [
self subclassResponsibility
]
Now when someone sends a message to your class he'll get an error that this method should be implemented, and you must override it in subclasses.
Yes. All instance vars can be accessed by a subclass.
Ok, so the keyword messages like withdraw: amount can actually have multiple parameters like: withdraw: amount becauseOf: reason. So first of all you make an initialiser:
initWithBalance: aBalance customer: aCustomer number: aNumber [
self init.
balance := aBalance.
customer := aCustomer.
number := aNumber
]
You can keep interest := 0. in main init.
Then, to make your life better, you make a parameterised new and call parameterised init from there.
SavingsAccount class [
newWithBalance: aBalance customer: aCustomer number: aNumber [
^ self new initWithBalance: aBalance customer: aCustomer number: aNumber
]
]
Related
I have an abstract contract and I need to validate the parameters and return an error if not valid and also return the gas fees.
I created a modifier called checkNumber and inside I validate the _number and use require and revert.
CODE
//SPDX-License-Identifier: UNLICENSED
// Solidity files have to start with this pragma.
// It will be used by the Solidity compiler to validate its version.
pragma solidity >=0.7.0 <0.9.0;
struct Information {
uint number;
string avatar;
}
abstract contract AbstractCandidate {
uint[6] private _numbers = [1, 2, 3, 4, 5, 6];
string[6] private _avatars = [
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-1.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-2.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-3.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-4.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-5.png",
"https://raw.githubusercontent.com/thiagosaud/dApp-superior-electoral-court/main/temp/imgs/candidate-6.png"
];
modifier checkNumber(uint _number) {
string memory _errorMessage = "Candidate number is incorret!";
require(_number < 1 || _number > _numbers.length, _errorMessage);
if (_number < 1 || _number > _numbers.length) {
revert(_errorMessage);
}
_;
}
function getInformation(uint _number) external view checkNumber(_number) returns(Information memory) {
return Information({ avatar: _avatars[_number], number: _numbers[_number] });
}
}
According to this logic you will always get _errorMessage. For example in require statement if _number 0 you wont get err, but youll get it in if statement, because 0 < 1 and it fits to your if statement condition.
The error I am getting when I compile is: (ParserError: Expected primary expression.price = _price; * (1 ether);)
I would like to add a timestamp of 7 days for the promoter to add all funds to escrow in full not half to meet my conditions to book us. Is this even possible as a condition for us on our in? Do I need a multisig added to my smart contract to make it secure on both sides or is this code ok? Can anyone assist me in solving this problem?
//SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
contract Escrow {
//VARIBALES
enum State { NOT_INITIATED, AWAITING_PAYMENT, AWAITING_DELIVERY, COMPLETE }
State public currState;
bool public isBuyerIn;
bool public isSellerIn;
uint public price;
address public buyer;
address payable public seller;
//MODIFIERS
modifier onlyBuyer() {
require(msg.sender == buyer, "Only buyer can call this function"); _;
}
modifier escrowNotStarted() {
require(currState == State.NOT_INITIATED);
_;
}
//FUCTIONS
constructor(address _buyer, address payable _seller, uint _price){
buyer =_buyer;
seller = _seller;
price = _price; * (1 ether);
}
function initContract() escrowNotStarted public{
if(msg.sender == buyer) {
isBuyerIn = true;
}
if (msg.sender == seller) {
isSellerIn = true;
}
if (isBuyerIn && isSellerIn) {
currState = State.AWAITING_PAYMENT;
}
}
function deposit() onlyBuyer public payable {
require(currState == State.AWAITING_PAYMENT, "Already paiid");
require(msg.value == price, "Wrong deposit amount");
currState = State.AWAITING_DELIVERY;
}
function confimDelivery() onlyBuyer payable public {
require(currState == State.AWAITING_DELIVERY, "Cannot confirm delivery");
seller.transfer(price);
currState = State.COMPLETE;
}
function withdraw() onlyBuyer payable public {
require(currState == State.AWAITING_DELIVERY, "Cannot withdraw at this stage");
payable(msg.sender).transfer(price);
currState = State.COMPLETE;
}
}
You have an extra semicolon on the price assigning line in the constructor.
Replace
// original code, extra semicolon
price = _price; * (1 ether);
with
// updated code, removed the semicolon
price = _price * (1 ether);
I made a simple contract that stores ether and then can send ether. The function that sends ether has a requirement that only the owner of the contract can send ether from the contract.
The contract mysteriously fails to send ether every subsequent call after the first.
I created a function to retrieve the owner address value in the contract and it turns out that after the first function call, it changes the data to 0x000000000000000000000000000000000000000a
Sending function:
function SendToAddress (uint8 amt, address adr) isOwner {
/* Have we transferred over the maximum amount in
the last 24 hours? */
if ((now - dayStartTime) >= secondsInADay) {
dayStartTime = now;
curDayTransfer = 0;
}
if ((curDayTransfer + amt) < dayMaxTransfer) {
adr.transfer (amt);
walletBalance -= amt;
curDayTransfer += amt;
MoneyTransfer newTransfer;
newTransfer.amount = amt;
newTransfer.target = adr;
newTransfer.timeStamp = now;
if (transferHistory.length == 100) {
// Shift all of the transactions in the history list forward
// to make space for the transaction.
for (uint8 i = 1; i < 100; i++) {
transferHistory[i] = transferHistory[i-1];
}
transferHistory[0] = newTransfer;
} else {
transferHistory.push (newTransfer);
}
}
}
isOwner modifier:
modifier isOwner() {
require(msg.sender == creatorAddress);
_;
}
constructor:
constructor () public {
creatorAddress = msg.sender;
}
I assume the compiler gives you a warning on the line MoneyTransfer newTransfer; about implicitly storing the data in storage. If you explicitly use MoneyTransfer storage newTransfer;, then you'll get a warning that you're using an uninitialized storage reference. That means whatever values you put in newTransfer will overwrite whatever's in the first few storage slots.
Use MoneyTransfer memory newTransfer; instead.
// #param physicalAddress - the actual address of the home a host wants to list (not the ethereum address)
// #return _id - list of ids for homes
function listHomesByAddress(string _physicalAddress) public returns(uint [] _id ) {
uint [] results;
for(uint i = 0 ; i<homes.length; i++) {
if(keccak256(homes[i].physicalAddress) == keccak256(_physicalAddress) && homes[i].available == true) {
results.push(homes[i].id);
}
}
return results;
}
The result is supposed to be a list of ids which match the physical address entered however it does not filter through but returns all the available homes.
When I change to using String utils nothing changes.
Here is the whole code:
pragma solidity ^0.4.0;
import "browser/StringUtils.sol";
// #title HomeListing
contract HomeListing {
struct Home {
uint id;
string physicalAddress;
bool available;
}
Home[] public homes;
mapping (address => Home) hostToHome;
event HomeEvent(uint _id);
event Test(uint length);
constructor() {
}
// #param physicalAddress - the actual address of the home a host wants to list (not the ethereum address)
function addHome(string _physicalAddress) public {
uint _id = uint(keccak256(_physicalAddress, msg.sender));
homes.push(Home(_id, _physicalAddress, true));
}
// #param physicalAddress - the actual address of the home a host wants to list (not the ethereum address)
// #return _id - list of ids for homes
function listHomesByAddress(string _physicalAddress) public returns(uint [] _id ) {
uint [] results;
for(uint i = 0 ; i<homes.length; i++) {
string location = homes[i].physicalAddress;
if(StringUtils.equal(location,_physicalAddress )) {
results.push(homes[i].id);
}
}
return results;
}
}
The part giving you trouble is the line uint[] results;. Arrays declared as local variables reference storage memory by default. From the "What is the memory keyword" section of the Solidity docs:
There are defaults for the storage location depending on which type of variable it concerns:
state variables are always in storage
function arguments are in memory by default
local variables of struct, array or mapping type reference storage by default
local variables of value type (i.e. neither array, nor struct nor mapping) are stored in the stack
The result is you're referencing the first storage slot of your contract, which happens to be Home[] public homes. That's why you're getting the entire array back.
To fix the problem, you need to use a memory array. However, you have an additional problem in that you can't use dynamic memory arrays in Solidity. A workaround is to decide on a result size limit and declare your array statically.
Example (limited to 10 results):
function listHomesByAddress(string _physicalAddress) public view returns(uint[10]) {
uint [10] memory results;
uint j = 0;
for(uint i = 0 ; i<homes.length && j < 10; i++) {
if(keccak256(homes[i].physicalAddress) == keccak256(_physicalAddress) && homes[i].available == true) {
results[j++] = homes[i].id;
}
}
return results;
}
I want to create a contract between a Patient and Doctor where the first Patient fills his details like name, age, and problems he is facing. As soon as the above details are filled function createDoctor() is called and name, age and problems are sent as parameters to the Doctor. Considering the details filled by the patient the doctor sets the Physician and updates the problems faced by the patient if any. But I am not able to figure out the way to implement the above statement. Below is the code where I am asking the details of Patient and send it to the Doctor. Now I am not able to implement the Doctor part. Below is the code:
Patient.sol
pragma solidity ^0.4.6;
import './Doctor.sol';
contract Patient {
bytes private name = "";
bytes private dateOfBirth="NA";
bytes private problemFaced = "";
address public myDoctor;
// Event that is fired when patient is changed
event patientChanged(string whatChanged);
function createDoctor() public {
myDoctor = new Doctor(getName(), getDateOfBirth(), getGender(), problemFaced);
}
function ProblemFaced(string problems) public {
problemFaced = bytes(problems);
}
function getName() internal view returns (bytes) {
return name; // First_Name Last_Name
}
function getDateOfBirth() internal view returns (bytes) {
return dateOfBirth; // YYYYMMDD
}
function setName(string _name) public {
name = bytes(_name); // First_Name Last_Name
emit patientChanged("name changed"); // fire the event
}
function setDateOfBirth(string _dateOfBirth) public {
dateOfBirth = bytes(_dateOfBirth); // YYYYMMDD
emit patientChanged("dateOfBirth changed"); // fire the event
}
}
Doctor.sol
pragma solidity ^0.4.6;
contract Doctor {
// the address of the owner (the patient)
address public owner;
uint balance;
// address of physician that can add allergies
string public physician;
// name of the patient LAST^FIRST
string public patient_name;
string public patient_dob;
string public patient_gender;
string public patient_problem = '';
modifier isOwnerPatient {
require(msg.sender == owner);
_;
}
function Doctor(bytes _name, bytes _dob, bytes _gender, bytes _problems) {
owner = msg.sender;
patient_name = string(_name);
patient_dob = string(_dob);
patient_gender = string(_gender);
patient_problem = string(_problems);
}
function updateProblem(bytes _condition) {
patient_problem = strConcat(patient_problem, string(_condition));
}
// allows owner to set the physician that can add allergies
function SetPhysician(string _physician) isOwnerPatient {
physician = _physician;
}
function strConcat(string _a, string _b) internal pure returns (string){
bytes memory _ba = bytes(_a);
bytes memory _bb = bytes(_b);
string memory abcde = new string(_ba.length + 2 + _bb.length);
delete abcde;
bytes memory babcde = bytes(abcde);
uint k = 0;
for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
babcde[k++] = ",";
babcde[k++] = " ";
for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
return string(babcde);
}
}
Is there any possibility of using owner and msg.sender while executing above problem statement.