Skip to content

Commit 680534a

Browse files
authored
feat(cairo): add HypErc20DexCollateral extension (#5)
### Description - Add extension to HypErc20CollateralComponent which deposits the collateral to DEX (paradex specific) contract after approving the tokens. - Provides a helper function balance_on_behalf_of for reading the balance of the user in the dex ### Drive-by changes None ### Related issues fixes https://linear.app/hyperlane-xyz/issue/ENG-1281/dex-deposit-extension-for-paradex ### Backward compatibility Yes ### Testing Unit testing
1 parent 57ad864 commit 680534a

File tree

9 files changed

+634
-3
lines changed

9 files changed

+634
-3
lines changed

cairo/Scarb.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ readme = "../README.md"
1414
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
1515

1616
[workspace.dependencies]
17+
array = "0.4.0"
1718
assert_macros = "2.10.1"
1819
starknet = "2.10.1"
1920
alexandria_bytes = "0.4.0"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use alexandria_bytes::bytes::{Bytes, BytesTrait};
2+
use core::array::ArrayTrait;
3+
use core::serde::{Serde, SerdeTrait};
4+
5+
impl SerdeBytes of Serde<Bytes> {
6+
fn serialize(self: @Bytes, ref output: Array<felt252>) {
7+
// Serialize length first
8+
Serde::<usize>::serialize(@self.len(), ref output);
9+
10+
// Serialize data
11+
let mut i: usize = 0;
12+
while i < self.len() {
13+
Serde::<u8>::serialize(@self.at(i), ref output);
14+
i += 1;
15+
}
16+
}
17+
18+
fn deserialize(ref serialized: Span<felt252>) -> Option<Bytes> {
19+
// Deserialize length
20+
let len = Serde::<usize>::deserialize(ref serialized)?;
21+
22+
// Create bytes and append each byte
23+
let mut bytes = BytesTrait::new_empty();
24+
let mut i: usize = 0;
25+
while i < len {
26+
let byte = Serde::<u8>::deserialize(ref serialized)?;
27+
bytes.append_byte(byte);
28+
i += 1;
29+
}
30+
31+
Option::Some(bytes)
32+
}
33+
}
34+
35+
// Also implement for snapshots which are used in interfaces
36+
impl SerdeSnapshotBytes of Serde<@Bytes> {
37+
fn serialize(self: @@Bytes, ref output: Array<felt252>) {
38+
Serde::<Bytes>::serialize(@self, ref output)
39+
}
40+
41+
fn deserialize(ref serialized: Span<felt252>) -> Option<@Bytes> {
42+
Option::Some(@Serde::<Bytes>::deserialize(ref serialized)?)
43+
}
44+
}

cairo/crates/mocks/src/lib.cairo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod mock_account;
1111
pub mod mock_eth;
1212
pub mod mock_hyp_erc721_uri_storage;
1313
pub mod mock_mailbox;
14+
pub mod mock_paradex_dex;
1415
pub mod mock_rate_limited;
1516
pub mod mock_validator_announce;
1617
pub mod test_erc20;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use contracts::utils::utils::U256TryIntoContractAddress;
2+
use core::starknet::event::EventEmitter;
3+
use openzeppelin::token::erc20::interface::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait};
4+
use starknet::ContractAddress;
5+
use starknet::get_contract_address;
6+
7+
8+
#[starknet::interface]
9+
pub trait IMockParadexDex<TContractState> {
10+
fn deposit_on_behalf_of(
11+
ref self: TContractState,
12+
recipient: ContractAddress,
13+
token_address: ContractAddress,
14+
amount: felt252,
15+
) -> felt252;
16+
17+
fn set_hyperlane_token(ref self: TContractState, token_address: ContractAddress);
18+
19+
fn get_token_asset_balance(
20+
self: @TContractState, account: ContractAddress, token_address: ContractAddress,
21+
) -> felt252;
22+
}
23+
24+
#[starknet::contract]
25+
pub mod MockParadexDex {
26+
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
27+
use super::*;
28+
29+
pub mod Errors {
30+
pub const CALLER_NOT_HYPERLANE: felt252 = 'Caller not hyperlane';
31+
pub const INSUFFICIENT_ALLOWANCE: felt252 = 'Insufficient allowance';
32+
}
33+
34+
#[storage]
35+
struct Storage {
36+
hyperlane_token_address: ContractAddress,
37+
}
38+
39+
#[event]
40+
#[derive(Drop, starknet::Event)]
41+
pub enum Event {
42+
DepositSuccess: DepositSuccess,
43+
}
44+
45+
#[derive(Drop, starknet::Event)]
46+
pub struct DepositSuccess {
47+
pub token: ContractAddress,
48+
pub recipient: ContractAddress,
49+
pub amount: u256,
50+
}
51+
52+
#[constructor]
53+
fn constructor(ref self: ContractState) {}
54+
55+
56+
#[abi(embed_v0)]
57+
impl IMockParadexDexImpl of super::IMockParadexDex<ContractState> {
58+
fn set_hyperlane_token(ref self: ContractState, token_address: ContractAddress) {
59+
self.hyperlane_token_address.write(token_address);
60+
}
61+
62+
fn deposit_on_behalf_of(
63+
ref self: ContractState,
64+
recipient: ContractAddress,
65+
token_address: ContractAddress,
66+
amount: felt252,
67+
) -> felt252 {
68+
// check if the sender is the hyperlane token address
69+
assert(
70+
starknet::get_caller_address() != self.hyperlane_token_address.read(),
71+
Errors::CALLER_NOT_HYPERLANE,
72+
);
73+
74+
let token_dispatcher = ERC20ABIDispatcher { contract_address: token_address };
75+
// check for the allowance of the token
76+
let allowance = token_dispatcher
77+
.allowance(starknet::get_caller_address(), get_contract_address());
78+
let amount_u256: u256 = amount.try_into().unwrap();
79+
assert(allowance >= amount_u256, Errors::INSUFFICIENT_ALLOWANCE);
80+
token_dispatcher
81+
.transfer_from(
82+
starknet::get_caller_address(), starknet::get_contract_address(), amount_u256,
83+
);
84+
85+
self
86+
.emit(
87+
DepositSuccess {
88+
token: token_address, recipient: recipient, amount: amount_u256,
89+
},
90+
);
91+
return amount;
92+
}
93+
94+
fn get_token_asset_balance(
95+
self: @ContractState, account: ContractAddress, token_address: ContractAddress,
96+
) -> felt252 {
97+
let token_dispatcher = ERC20ABIDispatcher { contract_address: token_address };
98+
token_dispatcher.balance_of(starknet::get_contract_address()).try_into().unwrap()
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)