Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions chains/aztec/contracts/train/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ authors = [""]
compiler_version = ">=0.25.0"

[dependencies]
aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v3.0.0-devnet.2", directory="noir-projects/aztec-nr/aztec" }
token = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v3.0.0-devnet.2", directory="noir-projects/noir-contracts/contracts/app/token_contract" }
aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v3.0.0-devnet.20251212", directory="noir-projects/aztec-nr/aztec" }
token = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v3.0.0-devnet.20251212", directory="noir-projects/noir-contracts/contracts/app/token_contract" }
sha256 = { tag = "v0.2.1", git = "https://github.com/noir-lang/sha256" }
156 changes: 101 additions & 55 deletions chains/aztec/contracts/train/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
// @@@@@ @@@ @@@@@@@@@ @@@ @@@ @@@ @@@

mod lib;
mod types;
use dep::aztec::macros::aztec;

#[aztec]
pub contract Train {
use std::meta::derive;

use aztec::macros::{
events::event,
functions::{external, initializer, view},
storage::storage,
};
Expand All @@ -29,7 +29,54 @@ pub contract Train {
use dep::token::Token;

use crate::lib::{bytes_to_u128_limbs, u128_limbs_to_bytes};
use crate::types::events::{DstLocked, SrcLocked, TokenRedeemed, TokenRefunded};

// Events
#[event]
pub struct SrcLocked {
pub swap_id: Field,
pub hashlock: [u8; 32],
pub dst_chain: str<30>,
pub dst_address: str<90>,
pub dst_asset: str<30>,
pub sender: AztecAddress,
pub src_receiver: AztecAddress,
pub src_asset: str<30>,
pub amount: u128,
pub timelock: u64,
}

#[event]
pub struct DstLocked {
pub swap_id: Field,
pub htlc_id: Field,
pub hashlock: [u8; 32],
pub dst_chain: str<30>,
pub dst_address: str<90>,
pub dst_asset: str<30>,
pub sender: AztecAddress,
pub src_receiver: AztecAddress,
pub src_asset: str<30>,
pub amount: u128,
pub reward: u128,
pub reward_timelock: u64,
pub timelock: u64,
}

#[event]
pub struct TokenRefunded {
pub swap_id: Field,
pub htlc_id: Field,
}

#[event]
pub struct TokenRedeemed {
pub swap_id: Field,
pub htlc_id: Field,
pub redeem_address: AztecAddress,
pub secret_high: u128,
pub secret_low: u128,
pub hashlock: [u8; 32],
}

#[derive(Eq, Packable, Serialize, Deserialize)]
pub struct HTLC_Public {
Expand Down Expand Up @@ -74,12 +121,12 @@ pub contract Train {
) {
assert(amount > 0, "FundsNotSent");

let htlc_public_current = storage.contracts.at(swap_id).at(0).read();
let htlc_public_current = self.storage.contracts.at(swap_id).at(0).read();
assert(htlc_public_current.sender == AztecAddress::zero(), "SwapAlreadyInitialized");
assert(context.timestamp() + 1800 < timelock, "InvalidTimelock");
assert(self.context.timestamp() + 1800 < timelock, "InvalidTimelock");

let hashlock_tuple = (hashlock_high, hashlock_low);
let sender = context.msg_sender().expect(f"Sender must not be none!");
let sender = self.msg_sender().expect(f"Sender must not be none!");
let htlc_public = HTLC_Public {
amount: amount,
token: token,
Expand All @@ -94,16 +141,15 @@ pub contract Train {
reward: 0 as u128,
reward_timelock: 0 as u64,
};
storage.contracts.at(swap_id).at(0).write(htlc_public);
self.storage.contracts.at(swap_id).at(0).write(htlc_public);

// Transfer tokens from sender to contract
Token::at(token).transfer_in_public(sender, context.this_address(), amount, 0).call(&mut context);
self.call(Token::at(token).transfer_in_public(sender, self.address, amount, 0));

// Track this swap for the user
let sender = context.msg_sender().expect(f"Sender must not be none!");
let current_count = storage.user_swaps_count.at(sender).read();
storage.user_swaps.at(sender).at(current_count).write(swap_id);
storage.user_swaps_count.at(sender).write(current_count + 1);
let current_count = self.storage.user_swaps_count.at(sender).read();
self.storage.user_swaps.at(sender).at(current_count).write(swap_id);
self.storage.user_swaps_count.at(sender).write(current_count + 1);

let hashlock = u128_limbs_to_bytes(hashlock_high, hashlock_low);
let log_msg = SrcLocked {
Expand All @@ -112,21 +158,21 @@ pub contract Train {
dst_chain: dst_chain,
dst_address: dst_address,
dst_asset: dst_asset,
sender: context.msg_sender().expect(f"Sender must not be none!"),
sender: sender,
src_receiver: src_receiver,
src_asset: src_asset,
amount: amount,
timelock: timelock,
};

context.emit_public_log(log_msg.pack());
self.emit(log_msg);
}

#[external("public")]
fn refund(swap_id: Field, htlc_id: Field) {
let htlc_public = storage.contracts.at(swap_id).at(htlc_id).read();
let htlc_public = self.storage.contracts.at(swap_id).at(htlc_id).read();
assert(htlc_public.claimed == 1, "AlreadyClaimed");
assert(htlc_public.timelock < context.timestamp(), "NotPassedTimelock");
assert(htlc_public.timelock < self.context.timestamp(), "NotPassedTimelock");

let refund_amount = if htlc_public.reward != 0 {
htlc_public.amount + htlc_public.reward
Expand All @@ -135,13 +181,13 @@ pub contract Train {
};

// Transfer tokens back to sender
Token::at(htlc_public.token).transfer_in_public(
context.this_address(),
self.call(Token::at(htlc_public.token).transfer_in_public(
self.address,
htlc_public.sender,
refund_amount,
0
).call(&mut context);
));

let modified_htlc_public = HTLC_Public {
amount: htlc_public.amount,
token: htlc_public.token,
Expand All @@ -157,9 +203,9 @@ pub contract Train {
reward_timelock: htlc_public.reward_timelock,
};

storage.contracts.at(swap_id).at(htlc_id).write(modified_htlc_public);
self.storage.contracts.at(swap_id).at(htlc_id).write(modified_htlc_public);
let log_msg = TokenRefunded { swap_id, htlc_id };
context.emit_public_log(log_msg.pack());
self.emit(log_msg);
}

#[external("public")]
Expand Down Expand Up @@ -187,14 +233,14 @@ pub contract Train {
// Enforce reward >= 10% of amount: reward * 10 >= amount
assert(reward * 10 >= amount, "InvalidRewardAmount");

let htlc_public_current = storage.contracts.at(swap_id).at(htlc_id).read();
let htlc_public_current = self.storage.contracts.at(swap_id).at(htlc_id).read();
assert(htlc_public_current.sender == AztecAddress::zero(), "HTLCAlreadyExists");
assert(context.timestamp() + 900 < timelock, "InvalidTimelock");
assert(self.context.timestamp() + 900 < timelock, "InvalidTimelock");
assert(reward_timelock <= timelock, "InvalidRewardTimelock");
assert(reward_timelock > context.timestamp(), "InvalidRewardTimelock");
assert(reward_timelock > self.context.timestamp(), "InvalidRewardTimelock");

let hashlock_tuple = (hashlock_high, hashlock_low);
let sender = context.msg_sender().expect(f"Sender must not be none!");
let sender = self.msg_sender().expect(f"Sender must not be none!");
let htlc_public = HTLC_Public {
amount: amount,
token: token,
Expand All @@ -209,10 +255,10 @@ pub contract Train {
reward: reward,
reward_timelock: reward_timelock,
};
storage.contracts.at(swap_id).at(htlc_id).write(htlc_public);
self.storage.contracts.at(swap_id).at(htlc_id).write(htlc_public);

// Transfer tokens from sender to contract
Token::at(token).transfer_in_public(sender, context.this_address(), total_amount, 0).call(&mut context);
self.call(Token::at(token).transfer_in_public(sender, self.address, total_amount, 0));

let hashlock = u128_limbs_to_bytes(hashlock_high, hashlock_low);
let log_msg = DstLocked {
Expand All @@ -222,7 +268,7 @@ pub contract Train {
dst_chain: dst_chain,
dst_address: dst_address,
dst_asset: dst_asset,
sender: context.msg_sender().expect(f"Sender must not be none!"),
sender: sender,
src_receiver: src_receiver,
src_asset: src_asset,
amount: amount,
Expand All @@ -231,15 +277,15 @@ pub contract Train {
timelock: timelock,
};

context.emit_public_log(log_msg.pack());
self.emit(log_msg);
}

#[external("public")]
fn redeem(swap_id: Field, htlc_id: Field, secret_high: u128, secret_low: u128) {
let secret = u128_limbs_to_bytes(secret_high, secret_low);
let caller = context.msg_sender().expect(f"Sender must not be none!");
let caller = self.msg_sender().expect(f"Sender must not be none!");

let mut htlc_public = storage.contracts.at(swap_id).at(htlc_id).read();
let htlc_public = self.storage.contracts.at(swap_id).at(htlc_id).read();
assert(htlc_public.amount > 0, "HTLCNotExists");
let hashed_secret = sha256::sha256_var(secret, secret.len() as u64);
let hashed_secret_tuple = bytes_to_u128_limbs(hashed_secret);
Expand All @@ -264,55 +310,55 @@ pub contract Train {
reward_timelock: htlc_public.reward_timelock,
};

storage.contracts.at(swap_id).at(htlc_id).write(modified_htlc_public);
self.storage.contracts.at(swap_id).at(htlc_id).write(modified_htlc_public);

// Handle token transfers based on reward logic
if htlc_public.reward == 0 {
// No reward: transfer amount to src_receiver
Token::at(htlc_public.token).transfer_in_public(
context.this_address(),
self.call(Token::at(htlc_public.token).transfer_in_public(
self.address,
htlc_public.src_receiver,
htlc_public.amount,
0
).call(&mut context);
} else if htlc_public.reward_timelock > context.timestamp() {
));
} else if htlc_public.reward_timelock > self.context.timestamp() {
// Before reward timelock: amount to src_receiver, reward back to sender
Token::at(htlc_public.token).transfer_in_public(
context.this_address(),
self.call(Token::at(htlc_public.token).transfer_in_public(
self.address,
htlc_public.src_receiver,
htlc_public.amount,
0
).call(&mut context);
Token::at(htlc_public.token).transfer_in_public(
context.this_address(),
));
self.call(Token::at(htlc_public.token).transfer_in_public(
self.address,
htlc_public.sender,
htlc_public.reward,
0
).call(&mut context);
));
} else {
// After reward timelock
if caller == htlc_public.src_receiver {
// src_receiver gets amount + reward
Token::at(htlc_public.token).transfer_in_public(
context.this_address(),
self.call(Token::at(htlc_public.token).transfer_in_public(
self.address,
htlc_public.src_receiver,
htlc_public.amount + htlc_public.reward,
0
).call(&mut context);
));
} else {
// amount to src_receiver, reward to caller
Token::at(htlc_public.token).transfer_in_public(
context.this_address(),
self.call(Token::at(htlc_public.token).transfer_in_public(
self.address,
htlc_public.src_receiver,
htlc_public.amount,
0
).call(&mut context);
Token::at(htlc_public.token).transfer_in_public(
context.this_address(),
));
self.call(Token::at(htlc_public.token).transfer_in_public(
self.address,
caller,
htlc_public.reward,
0
).call(&mut context);
));
}
}

Expand All @@ -325,25 +371,25 @@ pub contract Train {
hashlock: u128_limbs_to_bytes(htlc_public.hashlock_high, htlc_public.hashlock_low),
};

context.emit_public_log(log_msg.pack());
self.emit(log_msg);
}

#[external("public")]
#[view]
fn has_htlc(swap_id: Field, htlc_id: Field) -> pub bool {
let htlc = storage.contracts.at(swap_id).at(htlc_id).read();
let htlc = self.storage.contracts.at(swap_id).at(htlc_id).read();
htlc.sender != AztecAddress::zero()
}

#[external("public")]
#[view]
fn get_htlc(swap_id: Field, htlc_id: Field) -> pub HTLC_Public {
storage.contracts.at(swap_id).at(htlc_id).read()
self.storage.contracts.at(swap_id).at(htlc_id).read()
}

#[external("public")]
#[view]
fn get_user_swaps_count(user: AztecAddress) -> pub Field {
storage.user_swaps_count.at(user).read()
self.storage.user_swaps_count.at(user).read()
}
}
1 change: 0 additions & 1 deletion chains/aztec/contracts/train/src/types.nr

This file was deleted.

Loading