Skip to content

Commit 56317f1

Browse files
feat: recycle implementation
1 parent 56cad55 commit 56317f1

File tree

5 files changed

+87
-4
lines changed

5 files changed

+87
-4
lines changed

packages/usdc_migration/src/interface.cairo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub trait IUSDCMigration<T> {
33
/// Exchange `amount` of legacy token for new token.
44
/// Precondition: Caller has approved the contract to spend `amount` of legacy token.
55
fn exchange_legacy_for_new(ref self: T, amount: u256);
6+
fn recycle(ref self: T);
67
}
78

89
#[starknet::interface]

packages/usdc_migration/src/lib.cairo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod errors;
22
pub mod events;
33
pub mod interface;
4+
pub(crate) mod starkgate_interface;
45

56
#[cfg(test)]
67
pub mod tests;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use starknet::{ContractAddress, EthAddress};
2+
3+
#[starknet::interface]
4+
pub(crate) trait ITokenBridge<TContractState> {
5+
fn get_l1_token(self: @TContractState, l2_token: ContractAddress) -> EthAddress;
6+
fn initiate_token_withdraw(
7+
ref self: TContractState, l1_token: EthAddress, l1_recipient: EthAddress, amount: u256,
8+
);
9+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#[starknet::contract]
2+
pub mod TokenBridgeMock {
3+
use starknet::{ContractAddress, EthAddress};
4+
5+
#[storage]
6+
struct Storage {
7+
l1_token_address: EthAddress,
8+
l2_token_address: ContractAddress,
9+
}
10+
11+
#[constructor]
12+
fn constructor(
13+
ref self: ContractState, l1_token_address: EthAddress, l2_token_address: ContractAddress,
14+
) {
15+
self.l1_token_address.write(l1_token_address);
16+
self.l2_token_address.write(l2_token_address);
17+
}
18+
19+
#[abi(embed_v0)]
20+
pub impl TokenBridgeMockImpl of ITokenBridge<ContractState> {
21+
fn get_l1_token(self: @ContractState, l2_token: ContractAddress) -> EthAddress {
22+
assert_eq!(l2_token, self.l2_token_address.read(), "Invalid L2 token address");
23+
self.l1_token_address.read()
24+
}
25+
26+
fn initiate_token_withdraw(
27+
ref self: ContractState, l1_token: EthAddress, l1_recipient: EthAddress, amount: u256,
28+
) {
29+
assert_eq!(l1_token, self.l1_token_address.read(), "Invalid L1 token address");
30+
}
31+
}
32+
}

packages/usdc_migration/src/usdc_migration.cairo

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod USDCMigration {
77
use usdc_migration::errors::USDCMigrationError;
88
use usdc_migration::events::USDCMigrationEvents;
99
use usdc_migration::interface::{IUSDCMigration, IUSDCMigrationConfig};
10+
use usdc_migration::starkgate_interface::{ITokenBridgeDispatcher, ITokenBridgeDispatcherTrait};
1011

1112
#[storage]
1213
struct Storage {
@@ -18,10 +19,14 @@ pub mod USDCMigration {
1819
l1_recipient: EthAddress,
1920
/// Address in L2 that gets the remaining USDC.
2021
owner_l2_address: ContractAddress,
21-
/// Token bridge address.
22-
starkgate_address: ContractAddress,
22+
/// Token bridge dispatcher.
23+
starkgate_dispatcher: ITokenBridgeDispatcher,
2324
/// The threshold amount of legacy token balance, that triggers sending to L1.
2425
legacy_threshold: u256,
26+
/// Amount to send to L1 per recycle.
27+
l1_transfer_unit: u256,
28+
/// L1 USDC token address.
29+
l1_usdc_token_address: EthAddress,
2530
}
2631

2732
#[event]
@@ -42,11 +47,12 @@ pub mod USDCMigration {
4247
) {
4348
let legacy_dispatcher = IERC20Dispatcher { contract_address: legacy_token };
4449
let new_dispatcher = IERC20Dispatcher { contract_address: new_token };
50+
let starkgate_dispatcher = ITokenBridgeDispatcher { contract_address: starkgate_address };
4551
self.legacy_token_dispatcher.write(legacy_dispatcher);
4652
self.new_token_dispatcher.write(new_dispatcher);
4753
self.l1_recipient.write(l1_recipient);
4854
self.owner_l2_address.write(owner_l2_address);
49-
self.starkgate_address.write(starkgate_address);
55+
self.starkgate_dispatcher.write(starkgate_dispatcher);
5056
self.legacy_threshold.write(legacy_threshold);
5157
// Infinite approval to l2 address for both legacy and new tokens.
5258
legacy_dispatcher.approve(spender: owner_l2_address, amount: MAX_U256);
@@ -74,7 +80,41 @@ pub mod USDCMigration {
7480
new_token_dispatcher.transfer(recipient: caller_address, :amount);
7581

7682
self.emit(USDCMigrationEvents::USDCMigratedEvent { user: caller_address, amount });
77-
// TODO: send to l1 if threshold is reached.
83+
self._recycle(:contract_address);
84+
}
85+
86+
fn recycle(ref self: ContractState) {
87+
let contract_address = get_contract_address();
88+
self._recycle(:contract_address);
89+
}
90+
}
91+
92+
#[generate_trait]
93+
impl InternalUSDCMigration of InternalUSDCMigrationTrait {
94+
fn _recycle(self: @ContractState, contract_address: ContractAddress) {
95+
let legacy_balance = self
96+
.legacy_token_dispatcher
97+
.read()
98+
.balance_of(account: contract_address);
99+
if (legacy_balance >= self.threshold.read()) {
100+
self.send_units_to_l1(amount: legacy_balance);
101+
}
102+
}
103+
104+
fn send_units_to_l1(self: @ContractState, mut amount: u256) {
105+
let starkgate_dispatcher = self.starkgate_dispatcher.read();
106+
let l1_recipient = self.l1_recipient.read();
107+
let l1_usdc_token_address = self.l1_usdc_token_address.read();
108+
let l1_transfer_unit = self.l1_transfer_unit.read();
109+
let threshold = self.threshold.read();
110+
111+
while (amount >= threshold) {
112+
starkgate_dispatcher
113+
.initiate_token_withdraw(
114+
l1_token: l1_usdc_token_address, :l1_recipient, :amount,
115+
);
116+
amount -= l1_transfer_unit;
117+
}
78118
}
79119
}
80120

0 commit comments

Comments
 (0)