Skip to content
Merged
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 contracts/contracts/ccip/ccipsend_executor/contract.tolk
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fun onBouncedMessage(in: InMessageBounced) {
fun init(onrampSend: OnRamp_Send, config: CCIPSendExecutor_Config): CCIPSendExecutor<CCIPSendExecutor_State_Initialized> {
val st = lazy CCIPSendExecutor_InitialData.fromCell(contract.getData());
return CCIPSendExecutor<CCIPSendExecutor_State_Initialized> {
messageID: st.messageId,
messageID: st.messageID,
onrampSend: onrampSend,
addresses: CCIPSendExecutor_Addresses {
onramp: st.onramp,
Expand Down Expand Up @@ -165,7 +165,7 @@ fun CCIPSendExecutor<T>.exitSuccessfully(self, fee: coins) {
value: 0,
dest: self.addresses.load().onramp,
body: OnRamp_ExecutorFinishedSuccessfully {
msgId: self.messageID,
msgID: self.messageID,
msg: self.onrampSend.msg,
metadata: self.onrampSend.metadata,
fee,
Expand Down
2 changes: 1 addition & 1 deletion contracts/contracts/ccip/ccipsend_executor/types.tolk
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import "../onramp/messages.tolk";

struct CCIPSendExecutor_InitialData {
onramp: address,
messageId: uint224,
messageID: uint224,
}

struct CCIPSendExecutor_Data {
Expand Down
28 changes: 16 additions & 12 deletions contracts/contracts/ccip/onramp/contract.tolk
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,7 @@ fun send(payload: OnRamp_Send, sender: address, jettonWallet: address? = null) {
val executeMsg = createMessage({
bounce: true,
value: 0,
dest: AutoDeployAddress {
stateInit: ContractState {
code: st.executor_code,
data: CCIPSendExecutor_InitialData {
onramp: contract.getAddress(),
messageId: st.currentMessageId,
}.toCell(),
}
// TODO use toShard so these contracts live in the same shard as the onramp
},
dest: executorAddress(st.executorCode, st.currentMessageId),
body: CCIPSendExecutor_Execute {
onrampSend: payload,
config: CCIPSendExecutor_Config {
Expand All @@ -118,9 +109,22 @@ fun send(payload: OnRamp_Send, sender: address, jettonWallet: address? = null) {
st.store();
}

@inline
fun executorAddress(executorCode: cell, messageID: int): AutoDeployAddress {
return AutoDeployAddress {
stateInit: ContractState {
code: executorCode,
data: CCIPSendExecutor_InitialData {
onramp: contract.getAddress(),
messageID: messageID
}.toCell(),
}
}
}

fun commit(payload: OnRamp_ExecutorFinishedSuccessfully, sender: address) {
var st = lazy OnRamp_Storage.load();
// TODO validate sender is executor msg.msgId
assert(executorAddress(st.executorCode, payload.msgID).addressMatches(sender)) throw Error.Unauthorized;

val ccipsend: Router_CCIPSend = payload.msg.load();
val metadata = payload.metadata;
Expand Down Expand Up @@ -187,7 +191,7 @@ fun commit(payload: OnRamp_ExecutorFinishedSuccessfully, sender: address) {

fun replyWithError(payload: OnRamp_ExecutorFinishedWithError, sender: address) {
var st = lazy OnRamp_Storage.load();
// TODO validate sender is executor msg.msgId
assert(executorAddress(st.executorCode, payload.msgID).addressMatches(sender)) throw Error.Unauthorized;

val ccipsend: Router_CCIPSend = payload.msg.load();
val metadata = payload.metadata;
Expand Down
2 changes: 1 addition & 1 deletion contracts/contracts/ccip/onramp/messages.tolk
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct (0x10000003) OnRamp_SetDynamicConfig {

// crc32('OnRamp_ExecutorFinishedSuccessfully')
struct (0xCFA6B336) OnRamp_ExecutorFinishedSuccessfully {
msgId: uint224,
msgID: uint224,
msg: Cell<Router_CCIPSend>
metadata: Metadata
fee: coins
Expand Down
2 changes: 1 addition & 1 deletion contracts/contracts/ccip/onramp/storage.tolk
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct OnRamp_Storage {
config: Cell<OnRamp_DynamicConfig>;

destChainConfigs: map<uint64, OnRamp_DestChainConfig>; // chainSelector -> DestChainConfig
executor_code: cell; // code for CCIPSendExecutor
executorCode: cell; // code for CCIPSendExecutor
currentMessageId: uint224;
}

Expand Down
82 changes: 2 additions & 80 deletions contracts/tests/ccip/CCIPRouter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,87 +531,9 @@ describe('Router', () => {
}
})

Copy link
Collaborator Author

@patricios-space patricios-space Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will refactor this test to fit inside the e2e below in a later PR: #332

it('doesnt lose balance on messageSent fees', async () => {
const initialOnRampBalance = (await blockchain.getContract(onRamp.address)).balance

const ccipSend: rt.CCIPSend = {
queryID: 1,
destChainSelector: CHAINSEL_EVM_TEST_90000001,
receiver: EVM_ADDRESS,
data: Cell.EMPTY,
tokenAmounts: [],
feeToken: TEST_TOKEN_ADDR,
extraArgs: rt.builder.data.extraArgs
.encode({
kind: 'generic-v2',
gasLimit: 100n,
allowOutOfOrderExecution: true,
})
.asCell(),
}

const originalSentValue = toNano('0.5')
const valueFromExecutor = toNano('0.4')
const ccipFee = toNano('0.01')
const result = await onRamp.sendExecutorFinishedSuccessfully(deployer.getSender(), {
value: valueFromExecutor,
body: {
messageID: 42n,
msg: rt.builder.message.in.ccipSend.encode(ccipSend).asCell(),
metadata: {
sender: deployer.address,
value: originalSentValue,
},
fee: ccipFee,
},
})

expect(result.transactions).toHaveTransaction({
from: deployer.address,
to: onRamp.address,
success: true,
})

expect(result.transactions).toHaveTransaction({
from: onRamp.address,
to: router.address,
success: true,
op: rt.Opcodes.messageSent,
})

expect(result.transactions).toHaveTransaction({
from: router.address,
to: deployer.address,
success: true,
op: rt.OutgoingOpcodes.ccipSendACK,
})

const finalOnRampBalance = (await blockchain.getContract(onRamp.address)).balance

const relayTX = result.transactions.find((tx) => {
return (
tx.inMessage != null &&
tx.inMessage != undefined &&
tx.inMessage.info.src != null &&
tx.inMessage.info.src != undefined &&
tx.inMessage.info.src instanceof Address &&
tx.inMessage.info.src.equals(deployer.address) &&
tx.inMessage.info.dest != null &&
tx.inMessage.info.dest != undefined &&
tx.inMessage.info.dest instanceof Address &&
tx.inMessage.info.dest.equals(onRamp.address) &&
tx.description.type === 'generic'
)
}) as BlockchainTransaction & {
inMessage: Message & { info: CommonMessageInfoInternal }
description: TransactionDescriptionGeneric
}
const rentFee = relayTX.description.storagePhase?.storageFeesCollected ?? 0n

expect(finalOnRampBalance).toBe(initialOnRampBalance - rentFee + ccipFee)
})

it('onramp arbitrary message passing', async () => {
// Track initial balance to verify fees are handled correctly
const initialOnRampBalance = (await blockchain.getContract(onRamp.address)).balance
const ccipSend: rt.CCIPSend = {
queryID: 1,
destChainSelector: CHAINSEL_EVM_TEST_90000001,
Expand Down
Loading