From 3982b047a29f1b33e8c512d1cb191c93362d6830 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Fri, 22 Sep 2023 14:56:38 +0200 Subject: [PATCH 1/4] Add GoldToken to dev genesis --- contracts/celo/celo.go | 3 +++ core/celo_genesis.go | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/contracts/celo/celo.go b/contracts/celo/celo.go index 116a8a1c7e..e7484c61c7 100644 --- a/contracts/celo/celo.go +++ b/contracts/celo/celo.go @@ -8,5 +8,8 @@ import _ "embed" //go:embed compiled/Registry.bin-runtime var RegistryBytecodeRaw []byte +//go:embed compiled/GoldToken.bin-runtime +var GoldTokenBytecodeRaw []byte + //go:embed compiled/Proxy.bin-runtime var ProxyBytecodeRaw []byte diff --git a/core/celo_genesis.go b/core/celo_genesis.go index 7367f54b48..aa8773853e 100644 --- a/core/celo_genesis.go +++ b/core/celo_genesis.go @@ -36,6 +36,10 @@ func celoGenesisAccounts() map[common.Address]GenesisAccount { if err != nil { panic(err) } + goldTokenBytecode, err := DecodeHex(contracts.GoldTokenBytecodeRaw) + if err != nil { + panic(err) + } proxyBytecode, err := DecodeHex(contracts.ProxyBytecodeRaw) if err != nil { panic(err) @@ -56,5 +60,17 @@ func celoGenesisAccounts() map[common.Address]GenesisAccount { Code: registryBytecode, Balance: big.NewInt(0), }, + common.HexToAddress("0xce12"): { // GoldToken Proxy + Code: proxyBytecode, + Storage: map[common.Hash]common.Hash{ + proxy_implementation_slot: common.HexToHash("0xce13"), + proxy_owner_slot: registry_owner, + }, + Balance: big.NewInt(0), + }, + common.HexToAddress("0xce13"): { // GoldToken Implementation + Code: goldTokenBytecode, + Balance: big.NewInt(0), + }, } } From 21cc354b684cf868d88a7c5539297697f39a9ed8 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Fri, 22 Sep 2023 14:56:57 +0200 Subject: [PATCH 2/4] Add balance to fixed dev account --- core/celo_genesis.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/core/celo_genesis.go b/core/celo_genesis.go index aa8773853e..19096cde23 100644 --- a/core/celo_genesis.go +++ b/core/celo_genesis.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" contracts "github.com/ethereum/go-ethereum/contracts/celo" contracts_config "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/crypto" ) // Decode 0x prefixed hex string from file (including trailing newline) @@ -25,6 +26,10 @@ func DecodeHex(hexbytes []byte) ([]byte, error) { return bytes, nil } +var DevPrivateKey, _ = crypto.HexToECDSA("2771aff413cac48d9f8c114fabddd9195a2129f3c2c436caa07e27bb7f58ead5") +var DevAddr = common.BytesToAddress(DevAddr32.Bytes()) +var DevAddr32 = common.HexToHash("0x42cf1bbc38BaAA3c4898ce8790e21eD2738c6A4a") + func celoGenesisAccounts() map[common.Address]GenesisAccount { // As defined in ERC-1967: Proxy Storage Slots (https://eips.ethereum.org/EIPS/eip-1967) var ( @@ -44,15 +49,17 @@ func celoGenesisAccounts() map[common.Address]GenesisAccount { if err != nil { panic(err) } - registry_owner := common.HexToHash("0x42cf1bbc38BaAA3c4898ce8790e21eD2738c6A4a") + devBalance, ok := new(big.Int).SetString("100000000000000000000", 10) + if !ok { + panic("Could not set devBalance!") + } return map[common.Address]GenesisAccount{ - // Celo Contracts contracts_config.RegistrySmartContractAddress: { // Registry Proxy Code: proxyBytecode, Storage: map[common.Hash]common.Hash{ - common.HexToHash("0x0"): registry_owner, // `_owner` slot in Registry contract + common.HexToHash("0x0"): DevAddr32, // `_owner` slot in Registry contract proxy_implementation_slot: common.HexToHash("0xce11"), - proxy_owner_slot: registry_owner, + proxy_owner_slot: DevAddr32, }, Balance: big.NewInt(0), }, @@ -64,7 +71,7 @@ func celoGenesisAccounts() map[common.Address]GenesisAccount { Code: proxyBytecode, Storage: map[common.Hash]common.Hash{ proxy_implementation_slot: common.HexToHash("0xce13"), - proxy_owner_slot: registry_owner, + proxy_owner_slot: DevAddr32, }, Balance: big.NewInt(0), }, @@ -72,5 +79,8 @@ func celoGenesisAccounts() map[common.Address]GenesisAccount { Code: goldTokenBytecode, Balance: big.NewInt(0), }, + DevAddr: { + Balance: devBalance, + }, } } From 56771d9f25bb113c1a1b0950edbd5f1558f4a42b Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Mon, 25 Sep 2023 07:59:17 +0200 Subject: [PATCH 3/4] Add go-based TokenDuality e2e test Remaining problems: * Expects that geth binary has been build * In some cases, geth keeps running, even though CommandContext sends a SIGKILL on cancel. --- e2e_test/e2e_test.go | 118 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 e2e_test/e2e_test.go diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go new file mode 100644 index 0000000000..becb870799 --- /dev/null +++ b/e2e_test/e2e_test.go @@ -0,0 +1,118 @@ +package e2e_test + +import ( + "context" + "math/big" + "os/exec" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/celo/abigen" + "github.com/ethereum/go-ethereum/contracts/config" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/require" +) + +func waitForTx(client *ethclient.Client, tx *types.Transaction) { + for { + receipt, err := client.TransactionReceipt(context.Background(), tx.Hash()) + if err == nil && receipt.Status == 1 { + break + } + time.Sleep(100 * time.Microsecond) + } +} + +// waitForEndpoint attempts to connect to an RPC endpoint until it succeeds. +// Copied from cmd/geth/run_test.go in order to avoid changing existing file +func waitForEndpoint(t *testing.T, endpoint string, timeout time.Duration) { + probe := func() bool { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + c, err := rpc.DialContext(ctx, endpoint) + if c != nil { + _, err = c.SupportedModules() + c.Close() + } + return err == nil + } + + start := time.Now() + for { + if probe() { + return + } + if time.Since(start) > timeout { + t.Fatal("endpoint", endpoint, "did not open within", timeout) + } + time.Sleep(200 * time.Millisecond) + } +} + +func getClient(ctx context.Context, t *testing.T) *ethclient.Client { + cmd := exec.CommandContext(ctx, "../build/bin/geth", "--dev", "--http", "--http.api", "eth,web3,net") + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + + endpoint := "http://127.0.0.1:8545" + waitForEndpoint(t, endpoint, 1000*time.Millisecond) + client, err := ethclient.Dial(endpoint) + if err != nil { + t.Fatal(err) + } + + return client +} + +func TestTokenDuality(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + client := getClient(ctx, t) + auth, err := bind.NewKeyedTransactorWithChainID(core.DevPrivateKey, big.NewInt(1337)) + if err != nil { + t.Fatal(err) + } + + // Verify that balance is zero before transfer + targetAddress := common.HexToAddress("0x1dead") + balance, err := client.BalanceAt(ctx, targetAddress, nil) + if err != nil { + t.Fatal(err) + } + require.Equal(t, int64(0), balance.Int64()) + + // Initialize contract wrappers + goldTokenAddress := common.HexToAddress("0xce12") + goldToken, err := abigen.NewGoldToken(goldTokenAddress, client) + if err != nil { + t.Fatal(err) + } + registry, err := abigen.NewRegistry(config.RegistrySmartContractAddress, client) + if err != nil { + t.Fatal(err) + } + + // Register GoldToken + if _, err := registry.SetAddressFor(auth, "GoldToken", goldTokenAddress); err != nil { + t.Fatal(err) + } + + // Transfer and check result + tx, err := goldToken.Transfer(auth, targetAddress, big.NewInt(101)) + if err != nil { + t.Fatal(err) + } + waitForTx(client, tx) + balance, err = client.BalanceAt(ctx, targetAddress, nil) + if err != nil { + t.Fatal(err) + } + require.Equal(t, int64(101), balance.Int64()) +} From 39ef95f8efb9798235c235bb7eed36d4d78f0a64 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Mon, 25 Sep 2023 08:02:42 +0200 Subject: [PATCH 4/4] Remove shell-based e2e test --- e2e_test/.gitignore | 2 - e2e_test/contracts/GoldToken.sol | 1147 ------------------------------ e2e_test/foundry.toml | 6 - e2e_test/test_token_duality.sh | 21 - 4 files changed, 1176 deletions(-) delete mode 100644 e2e_test/.gitignore delete mode 100644 e2e_test/contracts/GoldToken.sol delete mode 100644 e2e_test/foundry.toml delete mode 100755 e2e_test/test_token_duality.sh diff --git a/e2e_test/.gitignore b/e2e_test/.gitignore deleted file mode 100644 index 1e4ded714a..0000000000 --- a/e2e_test/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -cache -out diff --git a/e2e_test/contracts/GoldToken.sol b/e2e_test/contracts/GoldToken.sol deleted file mode 100644 index 8b31cad522..0000000000 --- a/e2e_test/contracts/GoldToken.sol +++ /dev/null @@ -1,1147 +0,0 @@ -pragma solidity ^0.5.13; - -/* - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with GSN meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -contract Context { - // Empty internal constructor, to prevent people from mistakenly deploying - // an instance of this contract, which should be used via inheritance. - constructor () internal { } - // solhint-disable-previous-line no-empty-blocks - - function _msgSender() internal view returns (address payable) { - return msg.sender; - } - - function _msgData() internal view returns (bytes memory) { - this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 - return msg.data; - } -} - -/** - * @dev Contract module which provides a basic access control mechanism, where - * there is an account (an owner) that can be granted exclusive access to - * specific functions. - * - * This module is used through inheritance. It will make available the modifier - * `onlyOwner`, which can be applied to your functions to restrict their use to - * the owner. - */ -contract Ownable is Context { - address private _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev Initializes the contract setting the deployer as the initial owner. - */ - constructor () internal { - address msgSender = _msgSender(); - _owner = msgSender; - emit OwnershipTransferred(address(0), msgSender); - } - - /** - * @dev Returns the address of the current owner. - */ - function owner() public view returns (address) { - return _owner; - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - require(isOwner(), "Ownable: caller is not the owner"); - _; - } - - /** - * @dev Returns true if the caller is the current owner. - */ - function isOwner() public view returns (bool) { - return _msgSender() == _owner; - } - - /** - * @dev Leaves the contract without owner. It will not be possible to call - * `onlyOwner` functions anymore. Can only be called by the current owner. - * - * NOTE: Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public onlyOwner { - emit OwnershipTransferred(_owner, address(0)); - _owner = address(0); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function transferOwnership(address newOwner) public onlyOwner { - _transferOwnership(newOwner); - } - - /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). - */ - function _transferOwnership(address newOwner) internal { - require(newOwner != address(0), "Ownable: new owner is the zero address"); - emit OwnershipTransferred(_owner, newOwner); - _owner = newOwner; - } -} - -/** - * @dev Wrappers over Solidity's arithmetic operations with added overflow - * checks. - * - * Arithmetic operations in Solidity wrap on overflow. This can easily result - * in bugs, because programmers usually assume that an overflow raises an - * error, which is the standard behavior in high level programming languages. - * `SafeMath` restores this intuition by reverting the transaction when an - * operation overflows. - * - * Using this library instead of the unchecked operations eliminates an entire - * class of bugs, so it's recommended to use it always. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 c = a + b; - require(c >= a, "SafeMath: addition overflow"); - - return c; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return sub(a, b, "SafeMath: subtraction overflow"); - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - Subtraction cannot overflow. - * - * _Available since v2.4.0._ - */ - function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - require(b <= a, errorMessage); - uint256 c = a - b; - - return c; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) { - return 0; - } - - uint256 c = a * b; - require(c / a == b, "SafeMath: multiplication overflow"); - - return c; - } - - /** - * @dev Returns the integer division of two unsigned integers. Reverts on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return div(a, b, "SafeMath: division by zero"); - } - - /** - * @dev Returns the integer division of two unsigned integers. Reverts with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - The divisor cannot be zero. - * - * _Available since v2.4.0._ - */ - function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - // Solidity only automatically asserts when dividing by 0 - require(b > 0, errorMessage); - uint256 c = a / b; - // assert(a == b * c + a % b); // There is no case in which this doesn't hold - - return c; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * Reverts when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return mod(a, b, "SafeMath: modulo by zero"); - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * Reverts with custom message when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - The divisor cannot be zero. - * - * _Available since v2.4.0._ - */ - function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - require(b != 0, errorMessage); - return a % b; - } -} - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. Does not include - * the optional functions; to access them see {ERC20Detailed}. - */ -interface IERC20 { - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address recipient, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -interface IAccounts { - function isAccount(address) external view returns (bool); - function voteSignerToAccount(address) external view returns (address); - function validatorSignerToAccount(address) external view returns (address); - function attestationSignerToAccount(address) external view returns (address); - function signerToAccount(address) external view returns (address); - function getAttestationSigner(address) external view returns (address); - function getValidatorSigner(address) external view returns (address); - function getVoteSigner(address) external view returns (address); - function hasAuthorizedVoteSigner(address) external view returns (bool); - function hasAuthorizedValidatorSigner(address) external view returns (bool); - function hasAuthorizedAttestationSigner(address) external view returns (bool); - - function setAccountDataEncryptionKey(bytes calldata) external; - function setMetadataURL(string calldata) external; - function setName(string calldata) external; - function setWalletAddress(address, uint8, bytes32, bytes32) external; - function setAccount(string calldata, bytes calldata, address, uint8, bytes32, bytes32) external; - - function getDataEncryptionKey(address) external view returns (bytes memory); - function getWalletAddress(address) external view returns (address); - function getMetadataURL(address) external view returns (string memory); - function batchGetMetadataURL(address[] calldata) - external - view - returns (uint256[] memory, bytes memory); - function getName(address) external view returns (string memory); - - function authorizeVoteSigner(address, uint8, bytes32, bytes32) external; - function authorizeValidatorSigner(address, uint8, bytes32, bytes32) external; - function authorizeValidatorSignerWithPublicKey(address, uint8, bytes32, bytes32, bytes calldata) - external; - function authorizeValidatorSignerWithKeys( - address, - uint8, - bytes32, - bytes32, - bytes calldata, - bytes calldata, - bytes calldata - ) external; - function authorizeAttestationSigner(address, uint8, bytes32, bytes32) external; - function createAccount() external returns (bool); - - function setPaymentDelegation(address, uint256) external; - function getPaymentDelegation(address) external view returns (address, uint256); - function isSigner(address, address, bytes32) external view returns (bool); -} - -interface IFeeCurrencyWhitelist { - function addToken(address) external; - function getWhitelist() external view returns (address[] memory); -} - -interface IFreezer { - function isFrozen(address) external view returns (bool); -} - -interface IRegistry { - function setAddressFor(string calldata, address) external; - function getAddressForOrDie(bytes32) external view returns (address); - function getAddressFor(bytes32) external view returns (address); - function getAddressForStringOrDie(string calldata identifier) external view returns (address); - function getAddressForString(string calldata identifier) external view returns (address); - function isOneOf(bytes32[] calldata, address) external view returns (bool); -} - -interface IElection { - function electValidatorSigners() external view returns (address[] memory); - function electNValidatorSigners(uint256, uint256) external view returns (address[] memory); - function vote(address, uint256, address, address) external returns (bool); - function activate(address) external returns (bool); - function revokeActive(address, uint256, address, address, uint256) external returns (bool); - function revokeAllActive(address, address, address, uint256) external returns (bool); - function revokePending(address, uint256, address, address, uint256) external returns (bool); - function markGroupIneligible(address) external; - function markGroupEligible(address, address, address) external; - function allowedToVoteOverMaxNumberOfGroups(address) external returns (bool); - function forceDecrementVotes( - address, - uint256, - address[] calldata, - address[] calldata, - uint256[] calldata - ) external returns (uint256); - - // view functions - function getElectableValidators() external view returns (uint256, uint256); - function getElectabilityThreshold() external view returns (uint256); - function getNumVotesReceivable(address) external view returns (uint256); - function getTotalVotes() external view returns (uint256); - function getActiveVotes() external view returns (uint256); - function getTotalVotesByAccount(address) external view returns (uint256); - function getPendingVotesForGroupByAccount(address, address) external view returns (uint256); - function getActiveVotesForGroupByAccount(address, address) external view returns (uint256); - function getTotalVotesForGroupByAccount(address, address) external view returns (uint256); - function getActiveVoteUnitsForGroupByAccount(address, address) external view returns (uint256); - function getTotalVotesForGroup(address) external view returns (uint256); - function getActiveVotesForGroup(address) external view returns (uint256); - function getPendingVotesForGroup(address) external view returns (uint256); - function getGroupEligibility(address) external view returns (bool); - function getGroupEpochRewards(address, uint256, uint256[] calldata) - external - view - returns (uint256); - function getGroupsVotedForByAccount(address) external view returns (address[] memory); - function getEligibleValidatorGroups() external view returns (address[] memory); - function getTotalVotesForEligibleValidatorGroups() - external - view - returns (address[] memory, uint256[] memory); - function getCurrentValidatorSigners() external view returns (address[] memory); - function canReceiveVotes(address, uint256) external view returns (bool); - function hasActivatablePendingVotes(address, address) external view returns (bool); - - // only owner - function setElectableValidators(uint256, uint256) external returns (bool); - function setMaxNumGroupsVotedFor(uint256) external returns (bool); - function setElectabilityThreshold(uint256) external returns (bool); - - // only VM - function distributeEpochRewards(address, uint256, address, address) external; -} - -interface IGovernance { - function isVoting(address) external view returns (bool); - function getAmountOfGoldUsedForVoting(address account) external view returns (uint256); -} - -interface ILockedGold { - function incrementNonvotingAccountBalance(address, uint256) external; - function decrementNonvotingAccountBalance(address, uint256) external; - function getAccountTotalLockedGold(address) external view returns (uint256); - function getTotalLockedGold() external view returns (uint256); - function getPendingWithdrawals(address) - external - view - returns (uint256[] memory, uint256[] memory); - function getTotalPendingWithdrawals(address) external view returns (uint256); - function lock() external payable; - function unlock(uint256) external; - function relock(uint256, uint256) external; - function withdraw(uint256) external; - function slash( - address account, - uint256 penalty, - address reporter, - uint256 reward, - address[] calldata lessers, - address[] calldata greaters, - uint256[] calldata indices - ) external; - function isSlasher(address) external view returns (bool); -} - -interface IValidators { - function registerValidator(bytes calldata, bytes calldata, bytes calldata) - external - returns (bool); - function deregisterValidator(uint256) external returns (bool); - function affiliate(address) external returns (bool); - function deaffiliate() external returns (bool); - function updateBlsPublicKey(bytes calldata, bytes calldata) external returns (bool); - function registerValidatorGroup(uint256) external returns (bool); - function deregisterValidatorGroup(uint256) external returns (bool); - function addMember(address) external returns (bool); - function addFirstMember(address, address, address) external returns (bool); - function removeMember(address) external returns (bool); - function reorderMember(address, address, address) external returns (bool); - function updateCommission() external; - function setNextCommissionUpdate(uint256) external; - function resetSlashingMultiplier() external; - - // only owner - function setCommissionUpdateDelay(uint256) external; - function setMaxGroupSize(uint256) external returns (bool); - function setMembershipHistoryLength(uint256) external returns (bool); - function setValidatorScoreParameters(uint256, uint256) external returns (bool); - function setGroupLockedGoldRequirements(uint256, uint256) external returns (bool); - function setValidatorLockedGoldRequirements(uint256, uint256) external returns (bool); - function setSlashingMultiplierResetPeriod(uint256) external; - - // view functions - function getMaxGroupSize() external view returns (uint256); - function getCommissionUpdateDelay() external view returns (uint256); - function getValidatorScoreParameters() external view returns (uint256, uint256); - function getMembershipHistory(address) - external - view - returns (uint256[] memory, address[] memory, uint256, uint256); - function calculateEpochScore(uint256) external view returns (uint256); - function calculateGroupEpochScore(uint256[] calldata) external view returns (uint256); - function getAccountLockedGoldRequirement(address) external view returns (uint256); - function meetsAccountLockedGoldRequirements(address) external view returns (bool); - function getValidatorBlsPublicKeyFromSigner(address) external view returns (bytes memory); - function getValidator(address account) - external - view - returns (bytes memory, bytes memory, address, uint256, address); - function getValidatorGroup(address) - external - view - returns (address[] memory, uint256, uint256, uint256, uint256[] memory, uint256, uint256); - function getGroupNumMembers(address) external view returns (uint256); - function getTopGroupValidators(address, uint256) external view returns (address[] memory); - function getGroupsNumMembers(address[] calldata accounts) - external - view - returns (uint256[] memory); - function getNumRegisteredValidators() external view returns (uint256); - function groupMembershipInEpoch(address, uint256, uint256) external view returns (address); - - // only registered contract - function updateEcdsaPublicKey(address, address, bytes calldata) external returns (bool); - function updatePublicKeys(address, address, bytes calldata, bytes calldata, bytes calldata) - external - returns (bool); - function getValidatorLockedGoldRequirements() external view returns (uint256, uint256); - function getGroupLockedGoldRequirements() external view returns (uint256, uint256); - function getRegisteredValidators() external view returns (address[] memory); - function getRegisteredValidatorSigners() external view returns (address[] memory); - function getRegisteredValidatorGroups() external view returns (address[] memory); - function isValidatorGroup(address) external view returns (bool); - function isValidator(address) external view returns (bool); - function getValidatorGroupSlashingMultiplier(address) external view returns (uint256); - function getMembershipInLastEpoch(address) external view returns (address); - function getMembershipInLastEpochFromSigner(address) external view returns (address); - - // only VM - function updateValidatorScoreFromSigner(address, uint256) external; - function distributeEpochPaymentsFromSigner(address, uint256) external returns (uint256); - - // only slasher - function forceDeaffiliateIfValidator(address) external; - function halveSlashingMultiplier(address) external; - -} - -interface IRandom { - function revealAndCommit(bytes32, bytes32, address) external; - function randomnessBlockRetentionWindow() external view returns (uint256); - function random() external view returns (bytes32); - function getBlockRandomness(uint256) external view returns (bytes32); -} - -interface IAttestations { - function revoke(bytes32, uint256) external; - function withdraw(address) external; - - // view functions - function getUnselectedRequest(bytes32, address) external view returns (uint32, uint32, address); - function getAttestationIssuers(bytes32, address) external view returns (address[] memory); - function getAttestationStats(bytes32, address) external view returns (uint32, uint32); - function batchGetAttestationStats(bytes32[] calldata) - external - view - returns (uint256[] memory, address[] memory, uint64[] memory, uint64[] memory); - function getAttestationState(bytes32, address, address) - external - view - returns (uint8, uint32, address); - function getCompletableAttestations(bytes32, address) - external - view - returns (uint32[] memory, address[] memory, uint256[] memory, bytes memory); - function getAttestationRequestFee(address) external view returns (uint256); - function getMaxAttestations() external view returns (uint256); - function validateAttestationCode(bytes32, address, uint8, bytes32, bytes32) - external - view - returns (address); - function lookupAccountsForIdentifier(bytes32) external view returns (address[] memory); - function requireNAttestationsRequested(bytes32, address, uint32) external view; - - // only owner - function setAttestationRequestFee(address, uint256) external; - function setAttestationExpiryBlocks(uint256) external; - function setSelectIssuersWaitBlocks(uint256) external; - function setMaxAttestations(uint256) external; -} - -interface IExchange { - function buy( - uint256, - uint256, - bool - ) external returns (uint256); - - function sell( - uint256, - uint256, - bool - ) external returns (uint256); - - function exchange( - uint256, - uint256, - bool - ) external returns (uint256); - - function setUpdateFrequency(uint256) external; - - function getBuyTokenAmount(uint256, bool) external view returns (uint256); - - function getSellTokenAmount(uint256, bool) external view returns (uint256); - - function getBuyAndSellBuckets(bool) external view returns (uint256, uint256); -} - -interface IReserve { - function setTobinTaxStalenessThreshold(uint256) external; - - function addToken(address) external returns (bool); - - function removeToken(address, uint256) external returns (bool); - - function transferGold(address payable, uint256) external returns (bool); - - function transferExchangeGold(address payable, uint256) external returns (bool); - - function getReserveGoldBalance() external view returns (uint256); - - function getUnfrozenReserveGoldBalance() external view returns (uint256); - - function getOrComputeTobinTax() external returns (uint256, uint256); - - function getTokens() external view returns (address[] memory); - - function getReserveRatio() external view returns (uint256); - - function addExchangeSpender(address) external; - - function removeExchangeSpender(address, uint256) external; - - function addSpender(address) external; - - function removeSpender(address) external; -} - -/** - * @title This interface describes the functions specific to Celo Stable Tokens, and in the - * absence of interface inheritance is intended as a companion to IERC20.sol and ICeloToken.sol. - */ -interface IStableToken { - function mint(address, uint256) external returns (bool); - - function burn(uint256) external returns (bool); - - function setInflationParameters(uint256, uint256) external; - - function valueToUnits(uint256) external view returns (uint256); - - function unitsToValue(uint256) external view returns (uint256); - - function getInflationParameters() - external - view - returns ( - uint256, - uint256, - uint256, - uint256 - ); - - // NOTE: duplicated with IERC20.sol, remove once interface inheritance is supported. - function balanceOf(address) external view returns (uint256); -} - -interface ISortedOracles { - function addOracle(address, address) external; - function removeOracle(address, address, uint256) external; - function report(address, uint256, address, address) external; - function removeExpiredReports(address, uint256) external; - function isOldestReportExpired(address token) external view returns (bool, address); - function numRates(address) external view returns (uint256); - function medianRate(address) external view returns (uint256, uint256); - function numTimestamps(address) external view returns (uint256); - function medianTimestamp(address) external view returns (uint256); -} - -contract UsingRegistry is Ownable { - event RegistrySet(address indexed registryAddress); - - // solhint-disable state-visibility - bytes32 constant ACCOUNTS_REGISTRY_ID = keccak256(abi.encodePacked("Accounts")); - bytes32 constant ATTESTATIONS_REGISTRY_ID = keccak256(abi.encodePacked("Attestations")); - bytes32 constant DOWNTIME_SLASHER_REGISTRY_ID = keccak256(abi.encodePacked("DowntimeSlasher")); - bytes32 constant DOUBLE_SIGNING_SLASHER_REGISTRY_ID = keccak256( - abi.encodePacked("DoubleSigningSlasher") - ); - bytes32 constant ELECTION_REGISTRY_ID = keccak256(abi.encodePacked("Election")); - bytes32 constant EXCHANGE_REGISTRY_ID = keccak256(abi.encodePacked("Exchange")); - bytes32 constant FEE_CURRENCY_WHITELIST_REGISTRY_ID = keccak256( - abi.encodePacked("FeeCurrencyWhitelist") - ); - bytes32 constant FREEZER_REGISTRY_ID = keccak256(abi.encodePacked("Freezer")); - bytes32 constant GOLD_TOKEN_REGISTRY_ID = keccak256(abi.encodePacked("GoldToken")); - bytes32 constant GOVERNANCE_REGISTRY_ID = keccak256(abi.encodePacked("Governance")); - bytes32 constant GOVERNANCE_SLASHER_REGISTRY_ID = keccak256( - abi.encodePacked("GovernanceSlasher") - ); - bytes32 constant LOCKED_GOLD_REGISTRY_ID = keccak256(abi.encodePacked("LockedGold")); - bytes32 constant RESERVE_REGISTRY_ID = keccak256(abi.encodePacked("Reserve")); - bytes32 constant RANDOM_REGISTRY_ID = keccak256(abi.encodePacked("Random")); - bytes32 constant SORTED_ORACLES_REGISTRY_ID = keccak256(abi.encodePacked("SortedOracles")); - bytes32 constant STABLE_TOKEN_REGISTRY_ID = keccak256(abi.encodePacked("StableToken")); - bytes32 constant VALIDATORS_REGISTRY_ID = keccak256(abi.encodePacked("Validators")); - // solhint-enable state-visibility - - IRegistry public registry; - - modifier onlyRegisteredContract(bytes32 identifierHash) { - require(registry.getAddressForOrDie(identifierHash) == msg.sender, "only registered contract"); - _; - } - - modifier onlyRegisteredContracts(bytes32[] memory identifierHashes) { - require(registry.isOneOf(identifierHashes, msg.sender), "only registered contracts"); - _; - } - - /** - * @notice Updates the address pointing to a Registry contract. - * @param registryAddress The address of a registry contract for routing to other contracts. - */ - function setRegistry(address registryAddress) public onlyOwner { - require(registryAddress != address(0), "Cannot register the null address"); - registry = IRegistry(registryAddress); - emit RegistrySet(registryAddress); - } - - function getAccounts() internal view returns (IAccounts) { - return IAccounts(registry.getAddressForOrDie(ACCOUNTS_REGISTRY_ID)); - } - - function getAttestations() internal view returns (IAttestations) { - return IAttestations(registry.getAddressForOrDie(ATTESTATIONS_REGISTRY_ID)); - } - - function getElection() internal view returns (IElection) { - return IElection(registry.getAddressForOrDie(ELECTION_REGISTRY_ID)); - } - - function getExchange() internal view returns (IExchange) { - return IExchange(registry.getAddressForOrDie(EXCHANGE_REGISTRY_ID)); - } - - function getFeeCurrencyWhitelistRegistry() internal view returns (IFeeCurrencyWhitelist) { - return IFeeCurrencyWhitelist(registry.getAddressForOrDie(FEE_CURRENCY_WHITELIST_REGISTRY_ID)); - } - - function getFreezer() internal view returns (IFreezer) { - return IFreezer(registry.getAddressForOrDie(FREEZER_REGISTRY_ID)); - } - - function getGoldToken() internal view returns (IERC20) { - return IERC20(registry.getAddressForOrDie(GOLD_TOKEN_REGISTRY_ID)); - } - - function getGovernance() internal view returns (IGovernance) { - return IGovernance(registry.getAddressForOrDie(GOVERNANCE_REGISTRY_ID)); - } - - function getLockedGold() internal view returns (ILockedGold) { - return ILockedGold(registry.getAddressForOrDie(LOCKED_GOLD_REGISTRY_ID)); - } - - function getRandom() internal view returns (IRandom) { - return IRandom(registry.getAddressForOrDie(RANDOM_REGISTRY_ID)); - } - - function getReserve() internal view returns (IReserve) { - return IReserve(registry.getAddressForOrDie(RESERVE_REGISTRY_ID)); - } - - function getSortedOracles() internal view returns (ISortedOracles) { - return ISortedOracles(registry.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID)); - } - - function getStableToken() internal view returns (IStableToken) { - return IStableToken(registry.getAddressForOrDie(STABLE_TOKEN_REGISTRY_ID)); - } - - function getValidators() internal view returns (IValidators) { - return IValidators(registry.getAddressForOrDie(VALIDATORS_REGISTRY_ID)); - } -} - -contract CalledByVm { - modifier onlyVm() { - require(msg.sender == address(0), "Only VM can call"); - _; - } -} - -contract Initializable { - bool public initialized; - - constructor(bool testingDeployment) public { - if (!testingDeployment) { - initialized = true; - } - } - - modifier initializer() { - require(!initialized, "contract already initialized"); - initialized = true; - _; - } -} - -/** - * @title This interface describes the non- ERC20 shared interface for all Celo Tokens, and - * in the absence of interface inheritance is intended as a companion to IERC20.sol. - */ -interface ICeloToken { - function transferWithComment(address, uint256, string calldata) external returns (bool); - function name() external view returns (string memory); - function symbol() external view returns (string memory); - function decimals() external view returns (uint8); - function burn(uint256 value) external returns (bool); -} - -interface ICeloVersionedContract { - /** - * @notice Returns the storage, major, minor, and patch version of the contract. - * @return Storage version of the contract. - * @return Major version of the contract. - * @return Minor version of the contract. - * @return Patch version of the contract. - */ - function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256); -} - -contract GoldToken is - Initializable, - CalledByVm, - UsingRegistry, - IERC20, - ICeloToken, - ICeloVersionedContract -{ - using SafeMath for uint256; - - // Address of the TRANSFER precompiled contract. - // solhint-disable state-visibility - address constant TRANSFER = address(0xff - 2); - string constant NAME = "Celo native asset"; - string constant SYMBOL = "CELO"; - uint8 constant DECIMALS = 18; - uint256 internal totalSupply_; - // solhint-enable state-visibility - - mapping(address => mapping(address => uint256)) internal allowed; - - // Burn address is 0xdEaD because truffle is having buggy behaviour with the zero address - address constant BURN_ADDRESS = address(0x000000000000000000000000000000000000dEaD); - - event Transfer(address indexed from, address indexed to, uint256 value); - - event TransferComment(string comment); - - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @notice Sets initialized == true on implementation contracts - * @param test Set to true to skip implementation initialization - */ - constructor(bool test) public Initializable(test) {} - - /** - * @notice Returns the storage, major, minor, and patch version of the contract. - * @return Storage version of the contract. - * @return Major version of the contract. - * @return Minor version of the contract. - * @return Patch version of the contract. - */ - function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 2, 0); - } - - /** - * @notice Used in place of the constructor to allow the contract to be upgradable via proxy. - * @param registryAddress Address of the Registry contract. - */ - function initialize(address registryAddress) external initializer { - totalSupply_ = 0; - _transferOwnership(msg.sender); - setRegistry(registryAddress); - } - - /** - * @notice Transfers CELO from one address to another. - * @param to The address to transfer CELO to. - * @param value The amount of CELO to transfer. - * @return True if the transaction succeeds. - */ - // solhint-disable-next-line no-simple-event-func-name - function transfer(address to, uint256 value) external returns (bool) { - return _transferWithCheck(to, value); - } - - /** - * @notice Transfers CELO from one address to another with a comment. - * @param to The address to transfer CELO to. - * @param value The amount of CELO to transfer. - * @param comment The transfer comment - * @return True if the transaction succeeds. - */ - function transferWithComment(address to, uint256 value, string calldata comment) - external - returns (bool) - { - bool succeeded = _transferWithCheck(to, value); - emit TransferComment(comment); - return succeeded; - } - - /** - * @notice This function allows a user to burn a specific amount of tokens. - Burning is implemented by sending tokens to the burn address. - * @param value: The amount of CELO to burn. - * @return True if burn was successful. - */ - function burn(uint256 value) external returns (bool) { - // not using transferWithCheck as the burn address can potentially be the zero address - return _transfer(BURN_ADDRESS, value); - } - - /** - * @notice Approve a user to transfer CELO on behalf of another user. - * @param spender The address which is being approved to spend CELO. - * @param value The amount of CELO approved to the spender. - * @return True if the transaction succeeds. - */ - function approve(address spender, uint256 value) external returns (bool) { - require(spender != address(0), "cannot set allowance for 0"); - allowed[msg.sender][spender] = value; - emit Approval(msg.sender, spender, value); - return true; - } - - /** - * @notice Increases the allowance of another user. - * @param spender The address which is being approved to spend CELO. - * @param value The increment of the amount of CELO approved to the spender. - * @return True if the transaction succeeds. - */ - function increaseAllowance(address spender, uint256 value) external returns (bool) { - require(spender != address(0), "cannot set allowance for 0"); - uint256 oldValue = allowed[msg.sender][spender]; - uint256 newValue = oldValue.add(value); - allowed[msg.sender][spender] = newValue; - emit Approval(msg.sender, spender, newValue); - return true; - } - - /** - * @notice Decreases the allowance of another user. - * @param spender The address which is being approved to spend CELO. - * @param value The decrement of the amount of CELO approved to the spender. - * @return True if the transaction succeeds. - */ - function decreaseAllowance(address spender, uint256 value) external returns (bool) { - uint256 oldValue = allowed[msg.sender][spender]; - uint256 newValue = oldValue.sub(value); - allowed[msg.sender][spender] = newValue; - emit Approval(msg.sender, spender, newValue); - return true; - } - - /** - * @notice Transfers CELO from one address to another on behalf of a user. - * @param from The address to transfer CELO from. - * @param to The address to transfer CELO to. - * @param value The amount of CELO to transfer. - * @return True if the transaction succeeds. - */ - function transferFrom(address from, address to, uint256 value) external returns (bool) { - require(to != address(0), "transfer attempted to reserved address 0x0"); - require(value <= balanceOf(from), "transfer value exceeded balance of sender"); - require( - value <= allowed[from][msg.sender], - "transfer value exceeded sender's allowance for spender" - ); - - bool success; - (success, ) = TRANSFER.call.value(0).gas(gasleft())(abi.encode(from, to, value)); - require(success, "CELO transfer failed"); - - allowed[from][msg.sender] = allowed[from][msg.sender].sub(value); - emit Transfer(from, to, value); - return true; - } - - /** - * @notice Mints new CELO and gives it to 'to'. - * @param to The account for which to mint tokens. - * @param value The amount of CELO to mint. - */ - function mint(address to, uint256 value) external onlyVm returns (bool) { - if (value == 0) { - return true; - } - - require(to != address(0), "mint attempted to reserved address 0x0"); - totalSupply_ = totalSupply_.add(value); - - bool success; - (success, ) = TRANSFER.call.value(0).gas(gasleft())(abi.encode(address(0), to, value)); - require(success, "CELO transfer failed"); - - emit Transfer(address(0), to, value); - return true; - } - - /** - * @return The name of the CELO token. - */ - function name() external view returns (string memory) { - return NAME; - } - - /** - * @return The symbol of the CELO token. - */ - function symbol() external view returns (string memory) { - return SYMBOL; - } - - /** - * @return The number of decimal places to which CELO is divisible. - */ - function decimals() external view returns (uint8) { - return DECIMALS; - } - - /** - * @return The total amount of CELO in existence, including what the burn address holds. - */ - function totalSupply() external view returns (uint256) { - return totalSupply_; - } - - /** - * @return The total amount of CELO in existence, not including what the burn address holds. - */ - function circulatingSupply() external view returns (uint256) { - return totalSupply_.sub(getBurnedAmount()).sub(balanceOf(address(0))); - } - - /** - * @notice Gets the amount of owner's CELO allowed to be spent by spender. - * @param owner The owner of the CELO. - * @param spender The spender of the CELO. - * @return The amount of CELO owner is allowing spender to spend. - */ - function allowance(address owner, address spender) external view returns (uint256) { - return allowed[owner][spender]; - } - - /** - * @notice Increases the variable for total amount of CELO in existence. - * @param amount The amount to increase counter by - */ - function increaseSupply(uint256 amount) external onlyVm { - totalSupply_ = totalSupply_.add(amount); - } - - /** - * @notice Gets the amount of CELO that has been burned. - * @return The total amount of Celo that has been sent to the burn address. - */ - function getBurnedAmount() public view returns (uint256) { - return balanceOf(BURN_ADDRESS); - } - - /** - * @notice Gets the balance of the specified address. - * @param owner The address to query the balance of. - * @return The balance of the specified address. - */ - function balanceOf(address owner) public view returns (uint256) { - return owner.balance; - } - - /** - * @notice internal CELO transfer from one address to another. - * @param to The address to transfer CELO to. - * @param value The amount of CELO to transfer. - * @return True if the transaction succeeds. - */ - function _transfer(address to, uint256 value) internal returns (bool) { - require(value <= balanceOf(msg.sender), "transfer value exceeded balance of sender"); - - bool success; - (success, ) = TRANSFER.call.value(0).gas(gasleft())(abi.encode(msg.sender, to, value)); - require(success, "CELO transfer failed"); - emit Transfer(msg.sender, to, value); - return true; - } - - /** - * @notice Internal CELO transfer from one address to another. - * @param to The address to transfer CELO to. Zero address will revert. - * @param value The amount of CELO to transfer. - * @return True if the transaction succeeds. - */ - function _transferWithCheck(address to, uint256 value) internal returns (bool) { - require(to != address(0), "transfer attempted to reserved address 0x0"); - return _transfer(to, value); - } -} - diff --git a/e2e_test/foundry.toml b/e2e_test/foundry.toml deleted file mode 100644 index 0260703500..0000000000 --- a/e2e_test/foundry.toml +++ /dev/null @@ -1,6 +0,0 @@ -[profile.default] -src = "contracts" -out = "out" -libs = ["lib"] - -# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/e2e_test/test_token_duality.sh b/e2e_test/test_token_duality.sh deleted file mode 100755 index 17a47834a1..0000000000 --- a/e2e_test/test_token_duality.sh +++ /dev/null @@ -1,21 +0,0 @@ -#/bin/bash -set -eo pipefail - -# Before execution this script, run geth with a command like -# make geth && build/bin/geth --dev --http --http.api eth,web3,net console --exec "eth.sendTransaction({from: eth.coinbase, to: '0x42cf1bbc38BaAA3c4898ce8790e21eD2738c6A4a', value: web3.toWei(50, 'ether')}); admin.sleep(20)" - -export ETH_RPC_URL=http://127.0.0.1:8545 -ACC_ADDR=0x42cf1bbc38BaAA3c4898ce8790e21eD2738c6A4a -ACC_PRIVKEY=0x2771aff413cac48d9f8c114fabddd9195a2129f3c2c436caa07e27bb7f58ead5 - -REGISTRY_ADDR=0x000000000000000000000000000000000000ce10 - -# Deploy and register token -TOKEN_ADDR=$(forge create --private-key $ACC_PRIVKEY contracts/GoldToken.sol:GoldToken --constructor-args false | awk '/Deployed to/ {print $3}') -cast send --private-key $ACC_PRIVKEY $REGISTRY_ADDR 'function setAddressFor(string calldata identifier, address addr) external' GoldToken $TOKEN_ADDR -echo Account address: $ACC_ADDR, token address: $TOKEN_ADDR - -# Send token and check balance -cast balance 0x000000000000000000000000000000000000dEaD -cast send --private-key $ACC_PRIVKEY $TOKEN_ADDR 'function transfer(address to, uint256 value) external returns (bool)' 0x000000000000000000000000000000000000dEaD 100 -cast balance 0x000000000000000000000000000000000000dEaD