Skip to content

Commit

Permalink
Address some minor issues in the ZRC2->ERC20 proxy, add tests and ver…
Browse files Browse the repository at this point in the history
…ification. (#729)

* (feat) Documentation
(feat) Make zrc_proxy and decimals immutable
(feat) Pass on the result of gas() and not 21000
(feat) Burnable ERC20s
(feat) Tests for ERC20 proxy contract.
(feat) Deployment for ERC20 proxy is now via task and auto-verifies the contract

* (fix) We can now apparently overload call() - previously the compiler complained at me for this, but it now seems fine!
(fix) Don't limit deployment gas.

* (fmt) Usual argument with trunk - commented some useful values future authors may want to use.

* Update contracts/experimental/ERC20ProxyForZRC2/contracts/ScillaConnector.sol

Co-authored-by: Lukasz Kosiak <[email protected]>

* Update contracts/experimental/ERC20ProxyForZRC2/README.md

Co-authored-by: Lukasz Kosiak <[email protected]>

* Update contracts/experimental/ERC20ProxyForZRC2/test/basicProxyTest.ts

Co-authored-by: Lukasz Kosiak <[email protected]>

* Update contracts/experimental/ERC20ProxyForZRC2/test/basicProxyTest.ts

Co-authored-by: Lukasz Kosiak <[email protected]>

* (fix) Hopefully fix PR comments

---------

Co-authored-by: Lukasz Kosiak <[email protected]>
  • Loading branch information
rrw-zilliqa and lukozill authored Sep 9, 2024
1 parent 3e37cc5 commit dd4a604
Show file tree
Hide file tree
Showing 17 changed files with 6,315 additions and 1,023 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "contracts/experimental/ERC20ProxyForZRC2/lib/openzeppelin-contracts"]
path = contracts/experimental/ERC20ProxyForZRC2/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
1 change: 0 additions & 1 deletion contracts/experimental/ERC20ProxyForZRC2/.env.example

This file was deleted.

69 changes: 57 additions & 12 deletions contracts/experimental/ERC20ProxyForZRC2/README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,66 @@
# ERC20ProxyForZRC2 Contract

This is the contract to deploy a ERC20Proxy for a ZRC2 contract living in the scilla environment. It leverages the precompiles available in Zilliqa to interoperate between the 2 environments.
These contracts allow ZRC-2 tokens to look like ERC-20 tokens.

Make sure to specify the `zrc2_address` on the deployment file for the ERC20Proxy to be correctly deployed. This allows EVM to execute all desired functions on the ZRC2 as if it were a ERC20. Implementing IERC20 means that all existing DApps and wallets should be compatible with this token.
Unless you want to build using the `zilliqa-developer` version of `zilliqa-js`, or you encounter missing modules issues while running tests, install our dependencies with:

Make sure to also copy `.env.example` into `.env` and fill in the necessarily variables. Also ensure that `pnpm install` to install any necessary dependencies
```shell
pnpm --ignore-workspace i
```

The following are the deployment commands:
## Deploying a proxy

- Zilliqa Mainnet
You can deploy a proxy with:

```shell
pnpm exec hardhat run scripts/deploy.ts --network zq
```
```shell
export PRIVATE_KEY=<...>
pnpm exec hardhat deployProxy 0x5DD38E64dA8f7d541d8aF45fe00bF37F6a2c6195 --network zq-testnet
```

- Zilliqa Testnet
If your ZRC-2 is burnable (ie. supports the `Burn()` transition), you can use:

```shell
pnpm exec hardhat run scripts/deploy.ts --network zq-testnet
```
```shell
export PRIVATE_KEY=<...>
pnpm exec hardhat deployProxyBurnable 0x5DD38E64dA8f7d541d8aF45fe00bF37F6a2c6195 --network zq-testnet
```

The task should automatically verify these contracts to sourcify.

## Networks

Various networks are available in the `hardhat.conf.ts`:

- `zq-testnet` - the Zilliqa 1 testnet
- `zq` - the Zilliqa 1 mainnet
- `local-proxy` - a local proxy.

You can use the `local-proxy` network and run:

```sh
mitmweb --mode reverse:https://dev-api.zilliqa.com --no-web-open-browser --listen-port 5556 --web-port 5557
```

To monitor requests.

## Testing

To run the tests:

```shell
export PRIVATE_KEY=<...>
export TEST_KEY_1=<...>
export TEST_KEY_2=<...>
pnpm exec hardhat test --network zq-testnet
```

Each test has a number prefix so you can select them individually.

If you set the `CACHED` environment variable, we will use:

- `CACHED_ZRC2` - address of a ZRC-2
- `CACHED_ERC20` - address of an ERC-20

To run the tests; this saves you having to redeploy each time.

This allows you to run tests quickly, without waiting for contract
deployment.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@ library ScillaConnector {
uint private constant SCILLA_CALL_PRECOMPILE_ADDRESS = 0x5a494c53;
uint private constant SCILLA_STATE_READ_PRECOMPILE_ADDRESS = 0x5a494c92;

/**
* @dev Calls a ZRC2 contract function with one argument
* @param target The address of the ZRC2 contract
* @param tran_name The name of the function to call
* @param arg1 The first argument to the function
*/
function call(
address target,
string memory tran_name,
uint128 arg1
) internal {
bytes memory encodedArgs = abi.encode(
target,
tran_name,
CALL_SCILLA_WITH_THE_SAME_SENDER,
arg1
);
uint256 argsLength = encodedArgs.length;

assembly {
let alwaysSuccessForThisPrecompile := call(
gas(),
SCILLA_CALL_PRECOMPILE_ADDRESS,
0,
add(encodedArgs, 0x20),
argsLength,
0x20,
0
)
}
}


/**
* @dev Calls a ZRC2 contract function with two arguments
* @param target The address of the ZRC2 contract
Expand All @@ -30,7 +63,7 @@ library ScillaConnector {

assembly {
let alwaysSuccessForThisPrecompile := call(
21000,
gas(),
SCILLA_CALL_PRECOMPILE_ADDRESS,
0,
add(encodedArgs, 0x20),
Expand Down Expand Up @@ -68,7 +101,7 @@ library ScillaConnector {

assembly {
let alwaysSuccessForThisPrecompile := call(
21000,
gas(),
SCILLA_CALL_PRECOMPILE_ADDRESS,
0,
add(encodedArgs, 0x20),
Expand All @@ -95,7 +128,7 @@ library ScillaConnector {

assembly {
let alwaysSuccessForThisPrecompile := staticcall(
21000,
gas(),
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
Expand Down Expand Up @@ -123,7 +156,7 @@ library ScillaConnector {

assembly {
let alwaysSuccessForThisPrecompile := staticcall(
21000,
gas(),
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
Expand Down Expand Up @@ -152,7 +185,7 @@ library ScillaConnector {
uint256 output_len = output.length - 4;
assembly {
success := staticcall(
21000,
gas(),
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
Expand Down Expand Up @@ -187,7 +220,7 @@ library ScillaConnector {

assembly {
let alwaysSuccessForThisPrecompile := staticcall(
21000,
gas(),
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
Expand Down Expand Up @@ -224,7 +257,7 @@ library ScillaConnector {

assembly {
let alwaysSuccessForThisPrecompile := staticcall(
21000,
gas(),
SCILLA_STATE_READ_PRECOMPILE_ADDRESS,
add(encodedArgs, 0x20),
argsLength,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {ScillaConnector} from "./ScillaConnector.sol";

contract ZRC2ProxyForZRC2 is IERC20 {
contract ZRC2ERC20Proxy is IERC20 {
using ScillaConnector for address;
using SafeCast for uint256;

address public zrc2_proxy;
address immutable public zrc2_proxy;

// Additional variables useful for wallets
uint8 public decimals;
uint8 immutable public decimals;
string public symbol;
string public name;

Expand Down Expand Up @@ -56,6 +56,15 @@ contract ZRC2ProxyForZRC2 is IERC20 {
return true;
}

function _transferFrom(
address from,
address to,
uint256 tokens
) internal returns (bool) {
zrc2_proxy.call("TransferFrom", from, to, tokens.toUint128());
return true;
}

/**
* @notice Transfer tokens from one address to another
* @param from The address to transfer from
Expand All @@ -68,7 +77,7 @@ contract ZRC2ProxyForZRC2 is IERC20 {
address to,
uint256 tokens
) external returns (bool) {
zrc2_proxy.call("TransferFrom", from, to, tokens.toUint128());
_transferFrom(from, to, tokens);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {ScillaConnector} from "./ScillaConnector.sol";
import {ZRC2ERC20Proxy} from "./ZRC2ERC20Proxy.sol";

contract ZRC2ERC20ProxyBurnable is ZRC2ERC20Proxy {
using ScillaConnector for address;
using SafeCast for uint256;

/** Just chains down to the base constructor */
constructor(address zrc2_address) ZRC2ERC20Proxy(zrc2_address) { }

/**
* @dev Destroys a `value` amount of tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 value) public virtual {
uint128 value128 = value.toUint128();
zrc2_proxy.call("Burn", value128);
}

/**
* @dev Destroys a `value` amount of tokens from `account`, deducting from
* the caller's allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `value`.
*/
function burnFrom(address account, uint256 value) public virtual {
address self = address(msg.sender);

_transferFrom(account, self, value);
burn(value);
}
}
Loading

0 comments on commit dd4a604

Please sign in to comment.