Skip to content

Commit 46e7246

Browse files
wip: checking tx net flow
1 parent 92e7f47 commit 46e7246

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

contracts/tests/ccip/CCIPRouter.spec.ts

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Blockchain, BlockchainTransaction, SandboxContract, TreasuryContract } from '@ton/sandbox'
1+
import { Blockchain, printTransactionFees, SandboxContract, TreasuryContract } from '@ton/sandbox'
22
import {
33
toNano,
44
Address,
@@ -28,7 +28,8 @@ import { newWithdrawableSpec } from '../lib/funding/WithdrawableSpec'
2828
import * as ownable2step from '../../wrappers/libraries/access/Ownable2Step'
2929
import * as UpgradeableSpec from '../lib/versioning/UpgradeableSpec'
3030
import * as TypeAndVersionSpec from '../lib/versioning/TypeAndVersionSpec'
31-
import { dump } from '../utils/prettyPrint'
31+
import { dump, prettifyAddressesMap } from '../utils/prettyPrint'
32+
import { mapOpcode } from '../utils/opcodes'
3233

3334
const CHAINSEL_EVM_TEST_90000001 = 909606746561742123n
3435
const CHAINSEL_EVM_TEST_90000002 = 5548718428018410741n
@@ -675,6 +676,85 @@ describe('Router', () => {
675676
})
676677
},
677678
})
679+
680+
printTransactionFees(result.transactions, mapOpcode)
681+
const addresses = prettifyAddressesMap(result.transactions)
682+
683+
result.transactions.forEach((tx) => {
684+
if (
685+
tx.inMessage &&
686+
tx.inMessage.info.type === 'internal' &&
687+
tx.description.type === 'generic'
688+
) {
689+
const inValue = tx.inMessage.info.value.coins
690+
const outValue = tx.outMessages
691+
.values()
692+
.reduce(
693+
(acc, msg) => acc + (msg.info.type === 'internal' ? msg.info.value.coins : 0n),
694+
0n,
695+
)
696+
697+
const fees = {
698+
inFwdFee: tx.inMessage.info.forwardFee,
699+
gasFees:
700+
tx.description.computePhase.type === 'vm' ? tx.description.computePhase.gasFees : 0n,
701+
actionFees: tx.description.actionPhase?.totalActionFees ?? 0n,
702+
fwdFees: tx.description.actionPhase?.totalFwdFees ?? 0n,
703+
storageFees: tx.description.storagePhase?.storageFeesCollected ?? 0n,
704+
}
705+
const totalFees = [fees.actionFees, fees.gasFees, fees.storageFees].reduce(
706+
(a, b) => a + b,
707+
0n,
708+
)
709+
710+
console.log(
711+
`Balance check for tx from ${addresses.get(tx.inMessage.info.src.toRawString())} to ${addresses.get(tx.inMessage.info.dest.toRawString())}:\n`,
712+
)
713+
// table format
714+
console.table({
715+
'In Value': inValue,
716+
'Out Value': outValue,
717+
'In Fwd Fee': fees.inFwdFee,
718+
'Gas Fees': fees.gasFees,
719+
'Action Fees': fees.actionFees,
720+
'Fwd Fees': fees.fwdFees,
721+
'Storage Fees': fees.storageFees,
722+
'Total Fees': totalFees,
723+
'In Value - Out Value - Fees': inValue - outValue - totalFees,
724+
})
725+
}
726+
})
727+
728+
// Verify balance handling: OnRamp doesn't lose balance on messageSent fees
729+
const finalOnRampBalance = (await blockchain.getContract(onRamp.address)).balance
730+
const rentFees = result.transactions
731+
.filter((tx) => {
732+
return (
733+
tx.inMessage != null &&
734+
tx.inMessage != undefined &&
735+
tx.inMessage.info.dest != null &&
736+
tx.inMessage.info.dest != undefined &&
737+
tx.inMessage.info.dest instanceof Address &&
738+
tx.inMessage.info.dest.equals(router.address)
739+
)
740+
})
741+
.reduce((acc, tx) => {
742+
switch (tx.description.type) {
743+
case 'generic': {
744+
const rentFee = tx.description.storagePhase?.storageFeesCollected ?? 0n
745+
return acc + rentFee
746+
}
747+
case 'storage': {
748+
const rentFee = tx.description.storagePhase.storageFeesCollected
749+
return acc + rentFee
750+
}
751+
}
752+
return acc
753+
}, 0n)
754+
755+
// The final balance should be initial balance minus rent fees plus the fee that was paid
756+
// (the fee comes from the validated fee calculation above)
757+
expect(finalOnRampBalance).toBe(initialOnRampBalance - rentFees + amount.fee)
678758
}
679759
})
680760

contracts/tests/utils/opcodes.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as rt from '../../wrappers/ccip/Router'
2+
import * as onr from '../../wrappers/ccip/OnRamp'
3+
import * as fq from '../../wrappers/ccip/FeeQuoter'
4+
import * as sx from '../../wrappers/ccip/CCIPSendExecutor'
5+
// import * as rx from '../../wrappers/ccip/CCIPReceiveExecutor'
6+
import * as offr from '../../wrappers/ccip/OffRamp'
7+
8+
// Create a comprehensive opcode mapping
9+
const createOpcodeMapping = () => {
10+
const mapping: Record<number, string> = {}
11+
12+
for (const [ops, name] of [
13+
[rt.Opcodes, 'Router.in'],
14+
[rt.OutgoingOpcodes, 'Router.out'],
15+
[onr.Opcodes, 'OnRamp.in'],
16+
[fq.Opcodes, 'FeeQuoter.in'],
17+
[sx.Opcodes, 'SendExecutor.in'],
18+
// [rx.Opcodes, 'ReceiveExecutor.in'],
19+
[offr.Opcodes, 'OffRamp.in'],
20+
]) {
21+
for (const [key, value] of Object.entries(ops)) {
22+
mapping[value as number] = `${name}.${key}`
23+
}
24+
}
25+
26+
return mapping
27+
}
28+
29+
export const OPCODE_MAPPING = createOpcodeMapping()
30+
31+
// Useful to use with printTransactionFees from @ton/sandbox
32+
export const mapOpcode = (op: number): string | undefined => {
33+
return OPCODE_MAPPING[op]
34+
}

0 commit comments

Comments
 (0)