Skip to content

Commit 5ed1045

Browse files
authored
Merge pull request #2 from DefichainCommunity/main
Testnet v1
2 parents c6d1822 + ca43ce1 commit 5ed1045

14 files changed

Lines changed: 731 additions & 16 deletions

README

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Multi‑Wrapper Router Project
2+
3+
## Overview
4+
This project implements a *multi‑pair* cAsset/dToken wrapper contract and a corresponding router contract with upgradeability.
5+
The frontend is built with Rust + Dioxus + WASM to use with MetaMask.
6+
7+
## Structure
8+
- `contract/`: Solidity smart contract (upgradeable via UUPS), tests, deployment script
9+
- `frontend/`: Dioxus + Rust frontend, static assets
10+
11+
## Setup
12+
13+
### Contract
14+
#### Initialize
15+
1. Install Foundry (`curl -L https://foundry.paradigm.xyz | bash`, then `foundryup`).
16+
2. In `contract/`, install dependencies, compile:
17+
```bash
18+
forge install OpenZeppelin/openzeppelin-contracts-upgradeable
19+
forge build
20+
forge test
21+
22+
23+
#### Deploy
24+
1. Set environment variables in .env
25+
```ini
26+
RPC_URL=…
27+
PRIVATE_KEY=…
28+
2. Deploy:
29+
```bash
30+
source .env
31+
forge script script/DeploySystem{Mock,Testnet, Mainnet}.s.sol:DeploySystem \
32+
--rpc-url $RPC_URL \
33+
--broadcast \
34+
--verify
35+
36+
### Frontend
37+
1. In `frontend/`, build the WASM app (e.g., `dx build --release` or equivalent).
38+
2. Ensure the contract address (deployed) is updated in `frontend/src/main.rs`.
39+
3. Serve the frontend (e.g., `python3 -m http.server` in `frontend/target/dx/dioxus_meta_app/debug/web/public`).
40+
4. Connect MetaMask to the correct network, approve underlying tokens, and use the UI to Wrap/Unwrap.

contract/README

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#Overview
2+
3+
┌─────────────┐
4+
│ User │
5+
│ (approves) │
6+
└─────┬───────┘
7+
│ wrap/unwrap call
8+
9+
┌───────────────────────────┐
10+
│ CAssetDTokenWrapRouter │
11+
│ - AccessControl: MANAGER │
12+
│ - NonReentrant │
13+
└─────────┬─────────────────┘
14+
15+
│ transferFrom user → wrapper
16+
17+
┌────────────────────────────┐
18+
│ CAssetDTokenWrapUpgradeable│
19+
│ - AccessControl: WRAPPER │
20+
│ - NonReentrant │
21+
│ - Fee split (dToken) │
22+
│ - Calls treasuries │
23+
└─────────┬──────────────────┘
24+
│ ## wrap
25+
│ reclaim net dToken → dToken Treasury
26+
│ fee → feeRecipient
27+
│ dispense net amount → user (cAsset)
28+
│─────────────────────────┐
29+
│ │ dispense cAsset
30+
▼ ▼
31+
┌───────────────────────────┐┌─────────────────────────────────┐
32+
│ TokenTreasury (dToken) ││ TokenTreasury (cAsset) │
33+
│ - AccessControl: WRAPPER ││ - AccessControl: WRAPPER │
34+
│ - Keeps net + fee logic ││ - Receives cAsset from wrapper │
35+
└───────────────────────────┘└─────────────────────────────────┘
36+
37+
38+
#Running mock of defichain testent with anvil:
39+
## Start anvil with defichain testnet
40+
anvil -f 127.0.0.1:18551 --hardfork shanghai
41+
## Export first private key
42+
export PRIVATE_KEY=XYZ
43+
## Deploy anvil mock
44+
forge script --legacy script/DeploySystemMock.s.sol:DeploySystem --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast

contract/foundry.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[profile.default]
2+
src = "src"
3+
out = "out"
4+
ffi = true
5+
ast = true
6+
build_info = true
7+
extra_output = ["storageLayout"]
8+
libs = ["lib"]
9+
remappings = [
10+
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
11+
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
12+
"@openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/"
13+
]
14+
evm_version = 'shanghai'

contract/mocks/MockERC20.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5+
6+
contract MockERC20 is ERC20 {
7+
constructor(string memory n, string memory s) ERC20(n, s) {}
8+
9+
function mint(address to, uint256 amount) external {
10+
_mint(to, amount);
11+
}
12+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import "forge-std/Script.sol";
5+
import "../src/TokenTreasuryUpgradeable.sol";
6+
import "../src/CAssetDTokenWrapUpgradeable.sol";
7+
import "../src/CAssetDTokenWrapFactoryUpgradeable.sol";
8+
import "../src/CAssetDTokenWrapRouterUpgradeable.sol";
9+
import "../mocks/MockERC20.sol";
10+
11+
contract DeploySystem is Script {
12+
function run() external {
13+
vm.startBroadcast();
14+
15+
// Step 1: Deploy mock tokens
16+
MockERC20 tokenA = new MockERC20("dUSDC", "dUSDC");
17+
MockERC20 tokenB = new MockERC20("cUSDC", "cUSDC");
18+
19+
console.log("tokenA:", address(tokenA));
20+
console.log("tokenB:", address(tokenB));
21+
22+
// Step 2: Deploy logic implementations
23+
TokenTreasuryUpgradeable tokenTreasuryImpl = new TokenTreasuryUpgradeable();
24+
CAssetDTokenWrapUpgradeable wrapImpl = new CAssetDTokenWrapUpgradeable();
25+
console.log("TokenTreasury impl:", address(tokenTreasuryImpl));
26+
console.log("CAssetDTokenWrap impl:", address(wrapImpl));
27+
28+
// Step 3: Deploy router
29+
CAssetDTokenWrapRouterUpgradeable router = new CAssetDTokenWrapRouterUpgradeable();
30+
router.initialize(msg.sender);
31+
console.log("Router:", address(router));
32+
33+
// Step 4: Deploy factory (UUPS)
34+
CAssetDTokenWrapFactoryUpgradeable factory = new CAssetDTokenWrapFactoryUpgradeable();
35+
factory.initialize(address(tokenTreasuryImpl), address(wrapImpl), address(router), msg.sender);
36+
console.log("Factory:", address(factory));
37+
38+
// Step 5: Deploy a new wrap via factory
39+
address wrapAddr = factory.deployWrap(address(tokenA), address(tokenB),18, 18, 30, 40);
40+
console.log("Wrap instance:", wrapAddr);
41+
42+
// Step 6: Register with router
43+
router.registerWrapper(wrapAddr, address(tokenA), address(tokenB));
44+
45+
// Optional: fund and approve tokens
46+
tokenA.mint(msg.sender, 1000 ether);
47+
tokenB.mint(address(0x427cf764eb44f523F12798Ae48388F7f1c33277b), 1000 ether);
48+
49+
address dTokenTreasury = CAssetDTokenWrapUpgradeable(wrapAddr).dTokenTreasury();
50+
/* tokenA.approve(address(router), type(uint256).max); */
51+
tokenA.approve(address(dTokenTreasury), 1_000 ether);
52+
TokenTreasuryUpgradeable(dTokenTreasury).deposit(1_000 ether);
53+
54+
/* tokenB.approve(address(router), type(uint256).max); */
55+
56+
console.log("Setup complete!");
57+
58+
vm.stopBroadcast();
59+
}
60+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import "forge-std/Script.sol";
5+
import "../src/TokenTreasuryUpgradeable.sol";
6+
import "../src/CAssetDTokenWrapUpgradeable.sol";
7+
import "../src/CAssetDTokenWrapFactoryUpgradeable.sol";
8+
import "../src/CAssetDTokenWrapRouterUpgradeable.sol";
9+
import "../mocks/MockERC20.sol";
10+
11+
contract DeploySystem is Script {
12+
function run() external {
13+
vm.startBroadcast();
14+
15+
address cAsset = address(0x37386064e05d89FA6F4c9c1d2C05AbD6388aD750);
16+
address dToken = address(0xff0000000000000000000000000000000000005B);
17+
// Step 2: Deploy logic implementations
18+
TokenTreasuryUpgradeable tokenTreasuryImpl = new TokenTreasuryUpgradeable();
19+
CAssetDTokenWrapUpgradeable wrapImpl = new CAssetDTokenWrapUpgradeable();
20+
console.log("TokenTreasury impl:", address(tokenTreasuryImpl));
21+
console.log("CAssetDTokenWrap impl:", address(wrapImpl));
22+
23+
// Step 3: Deploy router
24+
CAssetDTokenWrapRouterUpgradeable router = new CAssetDTokenWrapRouterUpgradeable();
25+
router.initialize(msg.sender);
26+
console.log("Router:", address(router));
27+
28+
// Step 4: Deploy factory (UUPS)
29+
CAssetDTokenWrapFactoryUpgradeable factory = new CAssetDTokenWrapFactoryUpgradeable();
30+
factory.initialize(address(tokenTreasuryImpl), address(wrapImpl), address(router), msg.sender);
31+
console.log("Factory:", address(factory));
32+
33+
// Step 5: Deploy a new wrap via factory
34+
address wrapAddr = factory.deployWrap(dToken, cAsset, 18, 18, 30, 20);
35+
console.log("Wrap instance:", wrapAddr);
36+
37+
// Step 6: Register with router
38+
router.registerWrapper(wrapAddr, dToken, cAsset);
39+
40+
address dTokenTreasury = CAssetDTokenWrapUpgradeable(wrapAddr).dTokenTreasury();
41+
console.log("LUSDC instance:", dTokenTreasury);
42+
43+
console.log("Setup complete!");
44+
45+
vm.stopBroadcast();
46+
}
47+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import "forge-std/Script.sol";
5+
import {TokenTreasuryUpgradeable} from "../src/TokenTreasuryUpgradeable.sol";
6+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
8+
9+
10+
contract TestnetDeposit is Script {
11+
function run() external {
12+
vm.startBroadcast();
13+
address treasury = address(0x9D4F577fC58c885DB6B5f5A1a8fFdCECcAB5C40a);
14+
address dToken = address(0xff0000000000000000000000000000000000005B);
15+
16+
IERC20(dToken).approve(treasury, 1_000_000 ether);
17+
TokenTreasuryUpgradeable(treasury).deposit(1_000_000 ether);
18+
vm.stopBroadcast();
19+
}
20+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
5+
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
7+
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
8+
9+
import {CAssetDTokenWrapUpgradeable} from "./CAssetDTokenWrapUpgradeable.sol";
10+
11+
contract CAssetDTokenWrapFactoryUpgradeable is Initializable, OwnableUpgradeable, UUPSUpgradeable {
12+
address public router;
13+
address public tokenTreasuryImplementation;
14+
address public cAssetDTokenWrapImplementation;
15+
16+
address[] public allWraps;
17+
18+
event CAssetDTokenWrapDeployed(
19+
address indexed wrap,
20+
address indexed dToken,
21+
address indexed cAsset,
22+
address deployer
23+
);
24+
25+
function initialize(
26+
address _tokenTreasuryImpl,
27+
address _cAssetDTokenWrapImpl,
28+
address _router,
29+
address owner_
30+
) public initializer {
31+
__Ownable_init(owner_);
32+
__UUPSUpgradeable_init();
33+
34+
router = _router;
35+
tokenTreasuryImplementation = _tokenTreasuryImpl;
36+
cAssetDTokenWrapImplementation = _cAssetDTokenWrapImpl;
37+
}
38+
39+
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
40+
41+
function deployWrap(
42+
address _dTokenAddress,
43+
address _cAssetAddress,
44+
uint8 _dTokenDecimals,
45+
uint8 _cAssetDecimals,
46+
uint256 _dTokenInFeeBps,
47+
uint256 _dTokenOutFeeBps
48+
) external returns (address) {
49+
// use struct to solve Stack too deep.
50+
CAssetDTokenWrapUpgradeable.WrapInfo memory info = CAssetDTokenWrapUpgradeable.WrapInfo({
51+
dTokenAddress: _dTokenAddress,
52+
cAssetAddress: _cAssetAddress,
53+
dTokenDecimals: _dTokenDecimals,
54+
cAssetDecimals: _cAssetDecimals,
55+
dTokenInFeeBps: _dTokenInFeeBps,
56+
dTokenOutFeeBps: _dTokenOutFeeBps
57+
});
58+
ERC1967Proxy proxy = new ERC1967Proxy(
59+
cAssetDTokenWrapImplementation,
60+
abi.encodeWithSelector(
61+
CAssetDTokenWrapUpgradeable.initialize.selector,
62+
owner(), // factory owner as admin
63+
router, // router is the only contract to call (un)wrap
64+
tokenTreasuryImplementation,
65+
info
66+
)
67+
);
68+
69+
address wrapAddr = address(proxy);
70+
allWraps.push(wrapAddr);
71+
72+
emit CAssetDTokenWrapDeployed(wrapAddr, _dTokenAddress, _cAssetAddress, msg.sender);
73+
return wrapAddr;
74+
}
75+
76+
function getAllWraps() external view returns (address[] memory) {
77+
return allWraps;
78+
}
79+
}

0 commit comments

Comments
 (0)