@@ -93,34 +93,89 @@ export const WITHDRAW_LOCK_SECONDS = 604800;
9393
9494// ---- ABIs (exact signatures from the Morpheus source, verified on-chain) ----
9595export const depositPoolAbi = [
96- { type : "function" , name : "stake" , stateMutability : "nonpayable" , inputs : [
97- { name : "rewardPoolIndex" , type : "uint256" } , { name : "amount" , type : "uint256" } ,
98- { name : "claimLockEnd" , type : "uint128" } , { name : "referrer" , type : "address" } ,
99- ] , outputs : [ ] } ,
100- { type : "function" , name : "withdraw" , stateMutability : "nonpayable" , inputs : [
101- { name : "rewardPoolIndex" , type : "uint256" } , { name : "amount" , type : "uint256" } ,
102- ] , outputs : [ ] } ,
103- { type : "function" , name : "claim" , stateMutability : "payable" , inputs : [
104- { name : "rewardPoolIndex" , type : "uint256" } , { name : "receiver" , type : "address" } ,
105- ] , outputs : [ ] } ,
106- { type : "function" , name : "claimFor" , stateMutability : "payable" , inputs : [
107- { name : "poolId" , type : "uint256" } , { name : "user" , type : "address" } , { name : "receiver" , type : "address" } ,
108- ] , outputs : [ ] } ,
109- { type : "function" , name : "setClaimReceiver" , stateMutability : "nonpayable" , inputs : [
110- { name : "rewardPoolIndex" , type : "uint256" } , { name : "receiver" , type : "address" } ,
111- ] , outputs : [ ] } ,
112- { type : "function" , name : "getLatestUserReward" , stateMutability : "view" , inputs : [
113- { name : "rewardPoolIndex" , type : "uint256" } , { name : "user" , type : "address" } ,
114- ] , outputs : [ { type : "uint256" } ] } ,
115- { type : "function" , name : "usersData" , stateMutability : "view" , inputs : [
116- { name : "user" , type : "address" } , { name : "rewardPoolIndex" , type : "uint256" } ,
117- ] , outputs : [
118- { name : "lastStake" , type : "uint128" } , { name : "deposited" , type : "uint256" } ,
119- { name : "rate" , type : "uint256" } , { name : "pendingRewards" , type : "uint256" } ,
120- { name : "claimLockStart" , type : "uint128" } , { name : "claimLockEnd" , type : "uint128" } ,
121- { name : "virtualDeposited" , type : "uint256" } , { name : "lastClaim" , type : "uint128" } ,
122- { name : "referrer" , type : "address" } ,
123- ] } ,
96+ {
97+ type : "function" ,
98+ name : "stake" ,
99+ stateMutability : "nonpayable" ,
100+ inputs : [
101+ { name : "rewardPoolIndex" , type : "uint256" } ,
102+ { name : "amount" , type : "uint256" } ,
103+ { name : "claimLockEnd" , type : "uint128" } ,
104+ { name : "referrer" , type : "address" } ,
105+ ] ,
106+ outputs : [ ] ,
107+ } ,
108+ {
109+ type : "function" ,
110+ name : "withdraw" ,
111+ stateMutability : "nonpayable" ,
112+ inputs : [
113+ { name : "rewardPoolIndex" , type : "uint256" } ,
114+ { name : "amount" , type : "uint256" } ,
115+ ] ,
116+ outputs : [ ] ,
117+ } ,
118+ {
119+ type : "function" ,
120+ name : "claim" ,
121+ stateMutability : "payable" ,
122+ inputs : [
123+ { name : "rewardPoolIndex" , type : "uint256" } ,
124+ { name : "receiver" , type : "address" } ,
125+ ] ,
126+ outputs : [ ] ,
127+ } ,
128+ {
129+ type : "function" ,
130+ name : "claimFor" ,
131+ stateMutability : "payable" ,
132+ inputs : [
133+ { name : "poolId" , type : "uint256" } ,
134+ { name : "user" , type : "address" } ,
135+ { name : "receiver" , type : "address" } ,
136+ ] ,
137+ outputs : [ ] ,
138+ } ,
139+ {
140+ type : "function" ,
141+ name : "setClaimReceiver" ,
142+ stateMutability : "nonpayable" ,
143+ inputs : [
144+ { name : "rewardPoolIndex" , type : "uint256" } ,
145+ { name : "receiver" , type : "address" } ,
146+ ] ,
147+ outputs : [ ] ,
148+ } ,
149+ {
150+ type : "function" ,
151+ name : "getLatestUserReward" ,
152+ stateMutability : "view" ,
153+ inputs : [
154+ { name : "rewardPoolIndex" , type : "uint256" } ,
155+ { name : "user" , type : "address" } ,
156+ ] ,
157+ outputs : [ { type : "uint256" } ] ,
158+ } ,
159+ {
160+ type : "function" ,
161+ name : "usersData" ,
162+ stateMutability : "view" ,
163+ inputs : [
164+ { name : "user" , type : "address" } ,
165+ { name : "rewardPoolIndex" , type : "uint256" } ,
166+ ] ,
167+ outputs : [
168+ { name : "lastStake" , type : "uint128" } ,
169+ { name : "deposited" , type : "uint256" } ,
170+ { name : "rate" , type : "uint256" } ,
171+ { name : "pendingRewards" , type : "uint256" } ,
172+ { name : "claimLockStart" , type : "uint128" } ,
173+ { name : "claimLockEnd" , type : "uint128" } ,
174+ { name : "virtualDeposited" , type : "uint256" } ,
175+ { name : "lastClaim" , type : "uint128" } ,
176+ { name : "referrer" , type : "address" } ,
177+ ] ,
178+ } ,
124179] as const ;
125180
126181// ---- Claim / LayerZero fee (all read on-chain from L1SenderV2's config) ----
@@ -132,16 +187,57 @@ export const depositPoolAbi = [
132187export const L1_SENDER = getAddress ( "0x2Efd4430489e1a05A89c2f51811aC661B7E5FF84" ) ;
133188export const LZ_GATEWAY = getAddress ( "0x66A71Dcef29A0fFBDBE3c6a460a3B5BC225Cd675" ) ;
134189export const LZ_DST_CHAIN_ID = 110 ; // LayerZero v1 chain id for Arbitrum One
135- export const LZ_ADAPTER_PARAMS = "0x" as const ;
136-
137- export const lzEndpointAbi = [ {
138- type : "function" , name : "estimateFees" , stateMutability : "view" ,
139- inputs : [
140- { name : "dstChainId" , type : "uint16" } , { name : "userApplication" , type : "address" } ,
141- { name : "payload" , type : "bytes" } , { name : "payInZRO" , type : "bool" } , { name : "adapterParam" , type : "bytes" } ,
142- ] ,
143- outputs : [ { name : "nativeFee" , type : "uint256" } , { name : "zroFee" , type : "uint256" } ] ,
144- } ] as const ;
190+
191+ /**
192+ * Type-1 adapter params: `encodePacked(uint16(1), uint256(destinationGas))`.
193+ *
194+ * This used to be `"0x"`, which on classic LayerZero V1 meant "use the
195+ * pathway's configured default". The V1 endpoint now routes through the
196+ * V2-era UltraLightNode, which converts adapter params into worker options
197+ * and REJECTS empty bytes — `estimateFees` reverts with
198+ * `LZ_ULN_InvalidWorkerOptions(uint256)` (0x6592671c), which is exactly how
199+ * every MOR claim died: the fee quote threw before any transaction was sent.
200+ * Reproduced both ways by direct RPC call: `0x` reverts; type-1 params quote
201+ * fine (200k → 0.0000481 ETH, 350k → 0.0000511 ETH).
202+ *
203+ * 350k destination gas is deliberately conservative, NOT a documented
204+ * Morpheus figure: this constant only shapes the QUOTE (the value attached
205+ * to `claim()`), the send's real destination gas comes from the L1Sender's
206+ * own stored config, and the contracts refund excess `msg.value`. Over-quote
207+ * costs ~$0.01 refunded; under-quote kills the claim.
208+ */
209+ export const LZ_ADAPTER_PARAMS = `0x0001${ BigInt ( 350_000 ) . toString ( 16 ) . padStart ( 64 , "0" ) } ` as const ;
210+
211+ export const lzEndpointAbi = [
212+ {
213+ type : "function" ,
214+ name : "estimateFees" ,
215+ stateMutability : "view" ,
216+ inputs : [
217+ { name : "dstChainId" , type : "uint16" } ,
218+ { name : "userApplication" , type : "address" } ,
219+ { name : "payload" , type : "bytes" } ,
220+ { name : "payInZRO" , type : "bool" } ,
221+ { name : "adapterParam" , type : "bytes" } ,
222+ ] ,
223+ outputs : [
224+ { name : "nativeFee" , type : "uint256" } ,
225+ { name : "zroFee" , type : "uint256" } ,
226+ ] ,
227+ } ,
228+ // The ULN's revert vocabulary, so viem can decode a failure into a NAME
229+ // instead of showing users a raw selector with an openchain link.
230+ {
231+ type : "error" ,
232+ name : "LZ_ULN_InvalidWorkerOptions" ,
233+ inputs : [ { name : "cursor" , type : "uint256" } ] ,
234+ } ,
235+ {
236+ type : "error" ,
237+ name : "LZ_ULN_UnsupportedOptionType" ,
238+ inputs : [ { name : "optionType" , type : "uint256" } ] ,
239+ } ,
240+ ] as const ;
145241
146242/** MOR reward destination for a rider — a 2-recipient Arbitrum split (Gnars/athlete). */
147243export type MorpheusSponsor = { morSplit ?: Address } ;
0 commit comments