Skip to content

Commit 2420640

Browse files
committed
refactor: use function overloading instead of WithMsgValue variants
Replace quoteGreetingWithMsgValue/sendGreetingWithMsgValue with overloaded quoteGreeting/sendGreeting in both contracts. The short-param overload delegates to the full-param version, eliminating duplicated function bodies. Update tests, e2e scripts, and README to match the new signatures.
1 parent 375bb85 commit 2420640

7 files changed

Lines changed: 66 additions & 88 deletions

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Cross-chain messaging with Wormhole Executor, demonstrating both **off-chain** a
44

55
This repo covers two use cases:
66
- **EVM ↔ EVM** — both off-chain and on-chain quotes, Sepolia ↔ Base Sepolia
7-
- **EVM ↔ Solana** — off-chain quotes only (see [Cross-VM section](#cross-vm-evm--solana))
7+
- **EVM ↔ Solana**both off-chain and on-chain quotes (see [Cross-VM section](#cross-vm-evm--solana))
88

99
> **License:** Code provided "AS IS", without warranties. Audit before mainnet deployment.
1010
@@ -30,8 +30,6 @@ function quoteGreeting(uint16 targetChain, uint128 gasLimit, address quoterAddre
3030
function sendGreeting(..., address quoterAddress) external payable;
3131
```
3232

33-
> **Note:** On-chain quotes currently support EVM destination chains only.
34-
> For EVM → Solana, use `HelloWormhole` (off-chain signed quotes) instead.
3533

3634
## Deployed Contracts (Testnet)
3735

@@ -150,7 +148,7 @@ npx tsx test.ts # Off-chain quote E2E
150148

151149
## Cross-VM: EVM ↔ Solana
152150

153-
Uses `HelloWormhole` (off-chain quotes) on-chain quotes do not yet support Solana destinations.
151+
Both `HelloWormhole` (off-chain quotes) and `HelloWormholeOnChainQuote` (on-chain quotes) support Solana destinations.
154152

155153
See the [Solana demo repo](https://github.com/wormhole-foundation/demo-hello-executor-solana) for the Solana-side implementation.
156154

@@ -179,11 +177,11 @@ const emitterPdaBytes32 = '0x' + Buffer.from(emitterPda.toBytes()).toString('hex
179177

180178
### Sending to Solana
181179

182-
Use `sendGreetingWithMsgValue` with `msgValue` in **lamports** to cover rent and fees on Solana:
180+
Use the overloaded `sendGreeting` with `msgValue` in **lamports** to cover rent and fees on Solana:
183181

184182
```solidity
185183
// msgValue covers rent + fees on Solana (~0.015 SOL = 15_000_000 lamports)
186-
sequence = sendGreetingWithMsgValue(
184+
sequence = sendGreeting(
187185
greeting,
188186
1, // Wormhole chain ID for Solana
189187
500000, // compute units (not gas)

e2e/sendToSolana.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ const SOLANA_MSG_VALUE_LAMPORTS = 15_000_000n; // 0.015 SOL - a bit more for saf
3535

3636
// HelloWormhole ABI (just the functions we need)
3737
const ABI = [
38-
'function sendGreeting(string greeting, uint16 targetChain, uint128 gasLimit, uint256 totalCost, bytes signedQuote) external payable returns (uint64)',
39-
'function sendGreetingWithMsgValue(string greeting, uint16 targetChain, uint128 gasLimit, uint128 msgValue, uint256 totalCost, bytes signedQuote) external payable returns (uint64)',
38+
'function sendGreeting(string greeting, uint16 targetChain, uint128 gasLimit, uint128 msgValue, uint256 totalCost, bytes signedQuote) external payable returns (uint64)',
4039
'event GreetingSent(string greeting, uint16 targetChain, uint64 sequence)',
4140
];
4241

@@ -152,7 +151,7 @@ async function main() {
152151
// Send greeting with msgValue for Solana (in lamports)
153152
console.log('\nSending transaction with msgValue for Solana...');
154153
console.log(` msgValue: ${SOLANA_MSG_VALUE_LAMPORTS} lamports (${Number(SOLANA_MSG_VALUE_LAMPORTS) / 1e9} SOL)`);
155-
const tx = await contract.sendGreetingWithMsgValue(
154+
const tx = await contract.sendGreeting(
156155
greeting,
157156
CHAIN_ID_SOLANA,
158157
gasLimit,

e2e/sendToSolanaOnChainQuote.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
* Send greeting from Sepolia to Solana via Executor using ON-CHAIN QUOTE
44
*
55
* This uses the updated HelloWormholeOnChainQuote contract which supports
6-
* Solana destinations via sendGreetingWithMsgValue + quoteGreetingWithMsgValue.
6+
* Solana destinations via overloaded sendGreeting + quoteGreeting (with msgValue).
77
*
88
* Flow:
9-
* 1. quoteGreetingWithMsgValue — get on-chain quote (includes msgValue for Solana rent)
10-
* 2. sendGreetingWithMsgValue — publish Wormhole message + request relay via quoter router
9+
* 1. quoteGreeting(targetChain, gasLimit, msgValue, quoter) — get on-chain quote
10+
* 2. sendGreeting(greeting, targetChain, gasLimit, msgValue, totalCost, quoter) — publish + relay
1111
*
1212
* Usage:
1313
* npx tsx e2e/sendToSolanaOnChainQuote.ts "Hello from Sepolia (on-chain quote)!"
@@ -51,8 +51,8 @@ const SOLANA_MSG_VALUE_LAMPORTS = 15_000_000n; // ~0.015 SOL
5151
const GAS_LIMIT = 500_000n;
5252

5353
const ABI = [
54-
'function quoteGreetingWithMsgValue(uint16 targetChain, uint128 gasLimit, uint128 msgValue, address quoterAddress) external view returns (uint256 totalCost)',
55-
'function sendGreetingWithMsgValue(string calldata greeting, uint16 targetChain, uint128 gasLimit, uint128 msgValue, uint256 totalCost, address quoterAddress) external payable returns (uint64 sequence)',
54+
'function quoteGreeting(uint16 targetChain, uint128 gasLimit, uint128 msgValue, address quoterAddress) external view returns (uint256 totalCost)',
55+
'function sendGreeting(string calldata greeting, uint16 targetChain, uint128 gasLimit, uint128 msgValue, uint256 totalCost, address quoterAddress) external payable returns (uint64 sequence)',
5656
'function peers(uint16 chainId) external view returns (bytes32)',
5757
'event GreetingSent(string greeting, uint16 targetChain, uint64 sequence)',
5858
];
@@ -85,7 +85,7 @@ async function main() {
8585
console.log(` msgValue: ${SOLANA_MSG_VALUE_LAMPORTS} lamports (${Number(SOLANA_MSG_VALUE_LAMPORTS) / 1e9} SOL)`);
8686
console.log(` Quoter: ${QUOTER_ADDRESS}`);
8787

88-
const totalCost = await contract.quoteGreetingWithMsgValue(
88+
const totalCost = await contract.quoteGreeting(
8989
CHAIN_ID_SOLANA,
9090
GAS_LIMIT,
9191
SOLANA_MSG_VALUE_LAMPORTS,
@@ -100,7 +100,7 @@ async function main() {
100100

101101
// Step 2: Send greeting
102102
console.log('\nSending greeting with on-chain quote...');
103-
const tx = await contract.sendGreetingWithMsgValue(
103+
const tx = await contract.sendGreeting(
104104
greeting,
105105
CHAIN_ID_SOLANA,
106106
GAS_LIMIT,

src/HelloWormhole.sol

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -104,47 +104,25 @@ contract HelloWormhole is ExecutorSendReceiveQuoteOffChain, AccessControl {
104104
emit GreetingReceived(greeting, peerChain, peerAddress);
105105
}
106106

107-
// msgValue: lamports for Solana destinations, 0 for EVM
108-
function sendGreetingWithMsgValue(
107+
function sendGreeting(
109108
string calldata greeting,
110109
uint16 targetChain,
111110
uint128 gasLimit,
112-
uint128 msgValue,
113111
uint256 totalCost,
114112
bytes calldata signedQuote
115-
) public payable returns (uint64 sequence) {
116-
// Encode the greeting as bytes
117-
bytes memory payload = bytes(greeting);
118-
119-
// Solana enforces a 512-byte cap on incoming messages; fail early so the
120-
// relay fee is not spent on a delivery that will be rejected on Solana.
121-
if (targetChain == CHAIN_ID_SOLANA && payload.length > SOLANA_MAX_PAYLOAD_BYTES) {
122-
revert PayloadTooLargeForSolana(payload.length, SOLANA_MAX_PAYLOAD_BYTES);
123-
}
124-
125-
// Publish and relay the message to the target chain
126-
sequence = _publishAndRelay(
127-
payload,
128-
CONSISTENCY_LEVEL_INSTANT, // choose safe or finalized based on your needs
129-
totalCost,
130-
targetChain,
131-
msg.sender, // refund address
132-
signedQuote,
133-
gasLimit,
134-
msgValue, // pass through for SVM destinations (in lamports)
135-
"" // no extra relay instructions
136-
);
137-
138-
emit GreetingSent(greeting, targetChain, sequence);
113+
) external payable returns (uint64 sequence) {
114+
sequence = sendGreeting(greeting, targetChain, gasLimit, 0, totalCost, signedQuote);
139115
}
140116

117+
// msgValue: lamports for Solana destinations, 0 for EVM
141118
function sendGreeting(
142119
string calldata greeting,
143120
uint16 targetChain,
144121
uint128 gasLimit,
122+
uint128 msgValue,
145123
uint256 totalCost,
146124
bytes calldata signedQuote
147-
) external payable returns (uint64 sequence) {
125+
) public payable returns (uint64 sequence) {
148126
// Encode the greeting as bytes
149127
bytes memory payload = bytes(greeting);
150128

@@ -163,7 +141,7 @@ contract HelloWormhole is ExecutorSendReceiveQuoteOffChain, AccessControl {
163141
msg.sender, // refund address
164142
signedQuote,
165143
gasLimit,
166-
0, // no msg.value forwarding
144+
msgValue,
167145
"" // no extra relay instructions
168146
);
169147

src/HelloWormholeOnChainQuote.sol

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import {toUniversalAddress} from "wormhole-solidity-sdk/Utils.sol";
2121
* - Provides `quoteGreeting()` for on-chain cost estimation
2222
*
2323
* Supports both EVM and Solana destinations:
24-
* - EVM: use `sendGreeting` / `quoteGreeting` (msgValue = 0)
25-
* - Solana: use `sendGreetingWithMsgValue` / `quoteGreetingWithMsgValue`
24+
* - EVM: use `sendGreeting` / `quoteGreeting` (no msgValue)
25+
* - Solana: use overloaded `sendGreeting` / `quoteGreeting` with msgValue param
2626
* (msgValue in lamports for rent/fees)
2727
*/
2828
contract HelloWormholeOnChainQuote is ExecutorSendReceiveQuoteOnChain, AccessControl {
@@ -122,11 +122,12 @@ contract HelloWormholeOnChainQuote is ExecutorSendReceiveQuoteOnChain, AccessCon
122122
greeting = string(payload);
123123
}
124124

125+
// Emit an event with the greeting message and sender details
125126
emit GreetingReceived(greeting, peerChain, peerAddress);
126127
}
127128

128129
/**
129-
* @notice Get a quote for sending a greeting using on-chain quoter
130+
* @notice Get a quote for sending a greeting using on-chain quoter (EVM destinations)
130131
* @param targetChain The Wormhole chain ID of the destination
131132
* @param gasLimit Gas limit for execution on target chain
132133
* @param quoterAddress The on-chain quoter contract address
@@ -137,47 +138,51 @@ contract HelloWormholeOnChainQuote is ExecutorSendReceiveQuoteOnChain, AccessCon
137138
view
138139
returns (uint256 totalCost)
139140
{
140-
require(targetChain != CHAIN_ID_SOLANA, "Use quoteGreetingWithMsgValue for Solana");
141-
return _quoteGreeting(targetChain, gasLimit, 0, quoterAddress);
141+
return quoteGreeting(targetChain, gasLimit, 0, quoterAddress);
142142
}
143143

144144
/**
145145
* @notice Get a quote for sending a greeting with msgValue (needed for Solana destinations)
146+
* @param targetChain The Wormhole chain ID of the destination
147+
* @param gasLimit Gas limit for execution on target chain
146148
* @param msgValue For Solana: lamports for rent/priority fees. For EVM: 0.
149+
* @param quoterAddress The on-chain quoter contract address
150+
* @return totalCost The total cost including Wormhole message fee and executor fee
147151
*/
148-
function quoteGreetingWithMsgValue(uint16 targetChain, uint128 gasLimit, uint128 msgValue, address quoterAddress)
149-
external
150-
view
151-
returns (uint256 totalCost)
152-
{
153-
return _quoteGreeting(targetChain, gasLimit, msgValue, quoterAddress);
154-
}
155-
156-
function _quoteGreeting(uint16 targetChain, uint128 gasLimit, uint128 msgValue, address quoterAddress)
157-
internal
152+
function quoteGreeting(uint16 targetChain, uint128 gasLimit, uint128 msgValue, address quoterAddress)
153+
public
158154
view
159155
returns (uint256 totalCost)
160156
{
161157
bytes32 peerAddress = peers[targetChain];
162158
require(peerAddress != bytes32(0), "No peer set for target chain");
163159

160+
// Build relay instructions
164161
bytes memory relayInstructions = RelayInstructionLib.encodeGas(gasLimit, msgValue);
165162

163+
// Build request bytes (same format as _publishAndCompose uses)
166164
bytes memory requestBytes = RequestLib.encodeVaaMultiSigRequest(
167165
_chainId,
168166
toUniversalAddress(address(this)),
169167
0 // sequence placeholder - not needed for quote
170168
);
171169

170+
// Get executor quote from on-chain quoter
172171
uint256 executorFee = _executorQuoterRouter.quoteExecution(
173-
targetChain, peerAddress, address(0), quoterAddress, requestBytes, relayInstructions
172+
targetChain,
173+
peerAddress,
174+
address(0), // refund address not needed for quote
175+
quoterAddress,
176+
requestBytes,
177+
relayInstructions
174178
);
175179

180+
// Total = executor fee + Wormhole message fee
176181
totalCost = executorFee + _coreBridge.messageFee();
177182
}
178183

179184
/**
180-
* @notice Send a cross-chain greeting using on-chain quote
185+
* @notice Send a cross-chain greeting using on-chain quote (EVM destinations)
181186
* @param greeting The message to send
182187
* @param targetChain The Wormhole chain ID of the destination
183188
* @param gasLimit Gas limit for execution on target chain
@@ -192,49 +197,47 @@ contract HelloWormholeOnChainQuote is ExecutorSendReceiveQuoteOnChain, AccessCon
192197
uint256 totalCost,
193198
address quoterAddress
194199
) external payable returns (uint64 sequence) {
195-
require(targetChain != CHAIN_ID_SOLANA, "Use sendGreetingWithMsgValue for Solana");
196-
sequence = _sendGreeting(greeting, targetChain, gasLimit, 0, totalCost, quoterAddress);
200+
sequence = sendGreeting(greeting, targetChain, gasLimit, 0, totalCost, quoterAddress);
197201
}
198202

199203
/**
200204
* @notice Send a cross-chain greeting with msgValue (needed for Solana destinations)
205+
* @param greeting The message to send
206+
* @param targetChain The Wormhole chain ID of the destination
207+
* @param gasLimit Gas limit for execution on target chain
201208
* @param msgValue For Solana: lamports for rent/priority fees. For EVM: 0.
209+
* @param totalCost Total cost (Wormhole fee + executor fee from quoteGreeting)
210+
* @param quoterAddress The on-chain quoter contract address
211+
* @return sequence The Wormhole sequence number
202212
*/
203-
function sendGreetingWithMsgValue(
204-
string calldata greeting,
205-
uint16 targetChain,
206-
uint128 gasLimit,
207-
uint128 msgValue,
208-
uint256 totalCost,
209-
address quoterAddress
210-
) external payable returns (uint64 sequence) {
211-
sequence = _sendGreeting(greeting, targetChain, gasLimit, msgValue, totalCost, quoterAddress);
212-
}
213-
214-
function _sendGreeting(
213+
function sendGreeting(
215214
string calldata greeting,
216215
uint16 targetChain,
217216
uint128 gasLimit,
218217
uint128 msgValue,
219218
uint256 totalCost,
220219
address quoterAddress
221-
) internal returns (uint64 sequence) {
220+
) public payable returns (uint64 sequence) {
221+
// Encode the greeting as bytes
222222
bytes memory payload = bytes(greeting);
223223

224+
// Solana enforces a 512-byte cap on incoming messages; fail early so the
225+
// relay fee is not spent on a delivery that will be rejected on Solana.
224226
if (targetChain == CHAIN_ID_SOLANA && payload.length > SOLANA_MAX_PAYLOAD_BYTES) {
225227
revert PayloadTooLargeForSolana(payload.length, SOLANA_MAX_PAYLOAD_BYTES);
226228
}
227229

230+
// Publish and relay the message to the target chain using on-chain quote
228231
sequence = _publishAndRelay(
229232
payload,
230233
CONSISTENCY_LEVEL_INSTANT,
231234
totalCost,
232235
targetChain,
233-
msg.sender,
234-
quoterAddress,
236+
msg.sender, // refund address
237+
quoterAddress, // on-chain quoter instead of signedQuote
235238
gasLimit,
236239
msgValue,
237-
""
240+
"" // no extra relay instructions
238241
);
239242
emit GreetingSent(greeting, targetChain, sequence);
240243
}

test/HelloWormhole.t.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ contract HelloWormholeTest is Test {
120120

121121
vm.deal(address(this), 1 ether);
122122
vm.expectRevert(abi.encodeWithSelector(HelloWormhole.PayloadTooLargeForSolana.selector, 513, 512));
123-
helloWormholeSepolia.sendGreetingWithMsgValue{value: 0.01 ether}(
123+
helloWormholeSepolia.sendGreeting{value: 0.01 ether}(
124124
tooBig, CHAIN_ID_SOLANA, 500_000, 15_000_000, 0.01 ether, ""
125125
);
126126
}
@@ -151,7 +151,7 @@ contract HelloWormholeTest is Test {
151151
harness.executeVaa(payload, CHAIN_ID_SOLANA, emitterPda);
152152
}
153153

154-
function test_SendGreetingWithMsgValueRevertsWithMockExecutor() public {
154+
function test_SendGreetingOverloadedRevertsWithMockExecutor() public {
155155
vm.selectFork(sepoliaFork);
156156

157157
// Register a Solana peer (program ID for executor routing)
@@ -168,7 +168,7 @@ contract HelloWormholeTest is Test {
168168
// but confirms ABI compatibility)
169169
vm.deal(address(this), 1 ether);
170170
vm.expectRevert(); // Expected: mock executor can't process
171-
helloWormholeSepolia.sendGreetingWithMsgValue{value: 0.01 ether}(
171+
helloWormholeSepolia.sendGreeting{value: 0.01 ether}(
172172
"Hello Solana!",
173173
CHAIN_ID_SOLANA,
174174
gasLimit,

test/HelloWormholeOnChainQuote.t.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,23 +228,23 @@ contract HelloWormholeOnChainQuoteTest is Test {
228228

229229
vm.deal(address(this), 1 ether);
230230
vm.expectRevert(abi.encodeWithSelector(HelloWormholeOnChainQuote.PayloadTooLargeForSolana.selector, 513, 512));
231-
helloWormholeSepolia.sendGreetingWithMsgValue{value: 0.01 ether}(
231+
helloWormholeSepolia.sendGreeting{value: 0.01 ether}(
232232
tooBig, CHAIN_ID_SOLANA, 500_000, 15_000_000, 0.01 ether, QUOTER_ADDRESS
233233
);
234234
}
235235

236-
// ── quoteGreetingWithMsgValue ────────────────────────────────────────────
236+
// ── quoteGreeting (overloaded with msgValue) ─────────────────────────────
237237

238-
function test_QuoteGreetingWithMsgValueIncludesMsgValue() public {
238+
function test_QuoteGreetingOverloadedIncludesMsgValue() public {
239239
vm.selectFork(sepoliaFork);
240240

241241
bytes32 solanaProgramId = bytes32(0x62cf7e5a219d24a831e51b2c2417fa898920b930fd1c6947f3a4fc8feec1020f);
242242
helloWormholeSepolia.setPeer(CHAIN_ID_SOLANA, solanaProgramId);
243243

244244
uint256 quoteWithoutMsgValue =
245-
helloWormholeSepolia.quoteGreetingWithMsgValue(CHAIN_ID_SOLANA, 500_000, 0, QUOTER_ADDRESS);
245+
helloWormholeSepolia.quoteGreeting(CHAIN_ID_SOLANA, 500_000, 0, QUOTER_ADDRESS);
246246
uint256 quoteWithMsgValue =
247-
helloWormholeSepolia.quoteGreetingWithMsgValue(CHAIN_ID_SOLANA, 500_000, 15_000_000, QUOTER_ADDRESS);
247+
helloWormholeSepolia.quoteGreeting(CHAIN_ID_SOLANA, 500_000, 15_000_000, QUOTER_ADDRESS);
248248

249249
// Quote with msgValue should be higher than without
250250
assertGt(quoteWithMsgValue, quoteWithoutMsgValue, "msgValue should increase the quote");

0 commit comments

Comments
 (0)