Skip to content

feat(sdk-coin-tempo): add smart contract call support#8491

Merged
nayandas190 merged 1 commit intomasterfrom
cecho-697-tempo-contract-intrxn
Apr 13, 2026
Merged

feat(sdk-coin-tempo): add smart contract call support#8491
nayandas190 merged 1 commit intomasterfrom
cecho-697-tempo-contract-intrxn

Conversation

@nayandas190
Copy link
Copy Markdown
Contributor

Ticket: CECHO-697

Changes

  • Add RawContractCall type for pre-encoded calldata interactions
  • Add addRawCall() builder method for arbitrary contract calls (mint, approve, etc.)
  • Fix decodeCallToOperation() to return null for non-transferWithMemo calls instead of a lossy fabricated Tip20Operation (critical round-trip correctness fix)
  • Fix fromTip20Transaction() to populate operations and rawCalls separately
  • Update verifyTransaction() to handle mixed recipients (token transfers + contract calls)
  • Extend Tip20Transaction to expose getRawCalls(), include rawCalls in toJson/outputs
  • Add isValidHexData() utility for calldata validation
  • Update validateTransaction() to allow raw-call-only transactions
  • Add unit tests: addRawCall validation, round-trip (unsigned + signed), mixed batches, verifyTransaction with raw call recipients

@linear
Copy link
Copy Markdown

linear bot commented Apr 13, 2026

@nayandas190
Copy link
Copy Markdown
Contributor Author

@claude review this PR.

@nayandas190
Copy link
Copy Markdown
Contributor Author

@copilot Review

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 13, 2026

@copilot Review

Here's my review of the PR changes:


Summary

The PR adds smart contract call support to sdk-coin-tempo via a new RawContractCall type and addRawCall() builder method. The core approach (separating TIP-20 operations from raw contract calls, preserving calldata through round-trips, and updating verify logic) is sound. Tests are comprehensive. A few issues worth noting:


🔴 Call Order Not Preserved in Mixed Transactions

In buildImplementation, raw calls are always appended after all operations, regardless of insertion order:

const calls = this.operations.map((op) => this.operationToCall(op));
for (const rawCall of this.rawCalls) {
  calls.push({ to: rawCall.to, data: rawCall.data, value: ... });
}

So builder.addRawCall(x).addOperation(y) produces a transaction with [y, x] on-chain — not the user-specified [x, y]. For atomic batch calls where execution order matters (e.g., approve-then-mint), this silently reorders calls. The builder should maintain a single interleaved call list to preserve insertion order.


🟡 Type Safety: (recipient as any).data in verifyTransaction

if ((recipient as any).data) {

Using any bypasses TypeScript's type system and is fragile. Consider defining an extended recipient interface:

interface RawCallRecipient extends TransactionRecipient {
  data?: string;
}

Or use hasOwnProperty/'data' in recipient with a type guard instead of any.


🟡 Missing Validation for RawContractCall.value

addRawCall() validates to and data, but not value. If a caller passes value: "not-a-number", BigInt("not-a-number") will throw an uncaught error at build time. Should validate with something like if (call.value !== undefined && !/^\d+$/.test(call.value)) throw ....


🟡 _inputs Excludes Raw Call Native Value

In Tip20Transaction constructor, _inputs only sums token transfer amounts:

const totalUnits = operations.reduce((sum, op) => sum + amountToTip20Units(op.amount), 0n);

Native ETH value in raw calls (via call.value) is not included in _inputs. This can cause accounting discrepancies when raw calls carry native value.


🟢 value: "0" vs undefined After Deserialization

When a raw call with no value (undefined) goes through a round-trip, it comes back with value: "0" (from call.value.toString() where call.value is 0n). This is harmless functionally but changes the shape of the object. Can be fixed in fromTip20Transaction by conditionally setting value only when non-zero:

value: call.value !== 0n ? call.value.toString() : undefined

✅ What Looks Good

  • decodeCallToOperation returning null for non-transferWithMemo calls is the correct fix for the round-trip correctness issue
  • isValidHexData utility is clean and well-tested
  • The validateTransaction() change allowing raw-call-only transactions is correct
  • Test coverage is solid: round-trip (unsigned + signed), mixed batches, verification mismatches

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /opt/hostedtoolcache/node/24.14.1/x64/bin/node /opt/hostedtoolcache/node/24.14.1/x64/bin/node index.js --exec install /.bin/node-gyp-build (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@nayandas190 nayandas190 marked this pull request as ready for review April 13, 2026 14:48
@nayandas190 nayandas190 requested a review from a team as a code owner April 13, 2026 14:48
@sachushaji
Copy link
Copy Markdown
Contributor

@claude

Comment thread modules/sdk-coin-tempo/src/tempo.ts Outdated
@nayandas190 nayandas190 force-pushed the cecho-697-tempo-contract-intrxn branch from afe877b to 8ccd687 Compare April 13, 2026 15:16
Comment thread modules/sdk-coin-tempo/src/lib/transactionBuilder.ts
@nayandas190 nayandas190 merged commit ab7c52a into master Apr 13, 2026
24 checks passed
@nayandas190 nayandas190 deleted the cecho-697-tempo-contract-intrxn branch April 13, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants