diff --git a/cciplib/ton/tlbe/biguint.go b/cciplib/ton/tlbe/biguint.go index 1374b9f17..234fb8620 100644 --- a/cciplib/ton/tlbe/biguint.go +++ b/cciplib/ton/tlbe/biguint.go @@ -2,7 +2,6 @@ package tlbe // tlb extras import ( "encoding/json" - "errors" "fmt" "math/big" "strings" @@ -25,115 +24,82 @@ func AsUnsigned(v *big.Int, sz uint) *big.Int { return new(big.Int).And(v, mask) // interpret as uint sz } -// BigUint stores an unsigned integer with a fixed bit width for TLB encoding. -type BigUint struct { - Bits uint - Value *big.Int -} - -// loadFromCell enforces unsigned semantics for the configured bit width. -func (b *BigUint) loadFromCell(bits uint, loader *cell.Slice) error { - if b == nil { - return errors.New("BigUint pointer is nil") - } - if loader == nil { - return errors.New("cell loader is nil") - } - - width := bits - if width == 0 { - width = b.Bits - } - if width == 0 { - return errors.New("bit width must be greater than zero") - } +// --- Fixed-width unsigned integers --- +// +// Uint128/Uint160/Uint256 are boxed fixed-width unsigned integers used to model +// Tolk's uint128/uint160/uint256. They are deliberately NOT aliases of big.Int: +// big.Int is a struct containing a slice, so an alias is neither comparable nor +// usable as a map/dictionary key, and a pointer to it mis-keys (pointer +// identity) and panics when decoded through tonutils' reflection path. +// +// Each type stores its value as big-endian 64-bit words in a fixed-size array, +// which makes it a comparable value type. As a result these can be used directly +// as tlbe.Dict keys (matching Tolk map) and compare +// with == rather than via a helper. +// +// All three share the same codec (the uintWords* helpers below); each type only +// declares its width and delegates. - val, err := loader.LoadBigInt(width) - if err != nil { - return fmt.Errorf("failed to load bigint: %w", err) - } +// uintBytesFromBigInt converts v to exactly nbytes big-endian bytes, masking the +// input to bits bits first (so the sign bit is never misread). +func uintBytesFromBigInt(v *big.Int, bits uint, nbytes int) []byte { + out := make([]byte, nbytes) + AsUnsigned(v, bits).FillBytes(out) // big-endian, left-padded with zeros - b.Bits = width - b.Value = AsUnsigned(val, width) - return nil + return out } -// toCell serializes the stored number using unsigned semantics for width bits. -func (b BigUint) toCell(bits uint) (*cell.Cell, error) { - width := bits - if width == 0 { - width = b.Bits - } - if width == 0 { - return nil, errors.New("bit width must be greater than zero") - } - - val := b.Value - if val == nil { - val = new(big.Int) +// uintBytesToBigInt converts big-endian bytes to an unsigned big.Int. +func uintBytesToBigInt(b []byte) *big.Int { + res := new(big.Int).SetBytes(b) + if res.Sign() == 0 { + // Return the canonical zero form (abs == nil) so it compares equal to + // big.NewInt(0). + return new(big.Int) } - unsigned := AsUnsigned(val, width) + return res +} +// uintBytesToCell stores all bytes sequentially (8 bits each) into a single cell. +func uintBytesToCell(b []byte) (*cell.Cell, error) { builder := cell.BeginCell() - if err := builder.StoreBigInt(unsigned, width); err != nil { - return nil, fmt.Errorf("failed to store bigint: %w", err) + for i, by := range b { + if err := builder.StoreUInt(uint64(by), 8); err != nil { + return nil, fmt.Errorf("failed to store byte %d: %w", i, err) + } } return builder.EndCell(), nil } -// Uint160 is a 160-bit unsigned integer wrapper. -type Uint160 big.Int - -func NewUint160(v *big.Int) *Uint160 { - return (*Uint160)(AsUnsigned(v, 160)) -} - -func (*Uint160) BitsLen() uint { - return 160 -} - -// LoadFromCell implements tlb.Unmarshaler. -func (x *Uint160) LoadFromCell(loader *cell.Slice) error { - b := new(BigUint) - err := b.loadFromCell(x.BitsLen(), loader) - if err != nil { - return fmt.Errorf("failed to load Uint160 from cell: %w", err) +// uintBytesFromCell loads len(dst) sequential bytes into dst. +func uintBytesFromCell(dst []byte, loader *cell.Slice) error { + for i := range dst { + by, err := loader.LoadUInt(8) + if err != nil { + return fmt.Errorf("failed to load byte %d: %w", i, err) + } + dst[i] = byte(by) //nolint:gosec // G115: LoadUInt(8) always fits in a byte } - *x = Uint160(*b.Value) return nil } -// ToCell implements tlb.Marshaller. -func (x Uint160) ToCell() (*cell.Cell, error) { - b := BigUint{ - Bits: x.BitsLen(), - Value: (*big.Int)(&x), - } - return b.toCell(x.BitsLen()) +// uintBytesMarshalJSON emits the canonical hexadecimal string form. +func uintBytesMarshalJSON(b []byte) ([]byte, error) { + return []byte(`"0x` + uintBytesToBigInt(b).Text(16) + `"`), nil } -func (x *Uint160) MarshalJSON() ([]byte, error) { - if x == nil { - return []byte("null"), nil - } - - // Canonical output: hexadecimal string. - // Avoids precision/scientific-notation issues in intermediate tooling. - v := x.Value() - if v.Sign() < 0 || v.BitLen() > int(x.BitsLen()) { - return nil, errors.New("failed to marshal Uint160: out of range") - } +// parseUintJSON parses a JSON value (hex/decimal string, or number) into an +// unsigned big.Int that fits in bits bits. +func parseUintJSON(data []byte, bits uint) (*big.Int, error) { + var ( + b *big.Int + ok bool + ) - hex := v.Text(16) // lowercase - return []byte(`"0x` + hex + `"`), nil -} - -// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface. -func (x *Uint160) UnmarshalJSON(data []byte) error { - // Try JSON string first + // Try a JSON string first (hex or decimal). var s string if err := json.Unmarshal(data, &s); err == nil { base := 10 @@ -141,130 +107,162 @@ func (x *Uint160) UnmarshalJSON(data []byte) error { base = 16 s = s[2:] } - b, ok := new(big.Int).SetString(s, base) + b, ok = new(big.Int).SetString(s, base) if !ok { - return fmt.Errorf("failed to parse Uint160 string %q", s) + return nil, fmt.Errorf("failed to parse uint%d string %q", bits, s) } - - if b.Sign() < 0 || b.BitLen() > int(x.BitsLen()) { - return errors.New("failed to unmarshal Uint160 from JSON: out of range") + } else { + // Fallback: plain JSON number. + b = new(big.Int) + if err := b.UnmarshalJSON(data); err != nil { + return nil, fmt.Errorf("failed to unmarshal uint%d from JSON: %w", bits, err) } - *x = Uint160(*b) - return nil } - // Fallback: plain JSON number - b := new(big.Int) - if err := b.UnmarshalJSON(data); err != nil { - return fmt.Errorf("failed to unmarshal Uint160 from JSON: %w", err) + if b.Sign() < 0 || b.BitLen() > int(bits) { + return nil, fmt.Errorf("failed to unmarshal uint%d from JSON: out of range", bits) } - if b.Sign() < 0 || b.BitLen() > int(x.BitsLen()) { - return errors.New("failed to unmarshal Uint160 from JSON: out of range") - } - *x = Uint160(*b) - return nil + return b, nil } -func (x Uint160) Value() *big.Int { - return (*big.Int)(&x) +// Uint128 is a 128-bit unsigned integer (Tolk uint128, e.g. cursed-subject map +// keys: map). See the fixed-width section note above. +type Uint128 struct { + F [16]byte } -func (x Uint160) String() string { - return x.Value().String() +// NewUint128 builds a Uint128 from a big.Int, masking the input to 128 bits. +func NewUint128(v *big.Int) *Uint128 { + var a [16]byte + copy(a[:], uintBytesFromBigInt(v, 128, 16)) + + return &Uint128{F: a} } -// Uint256 is a 256-bit unsigned integer wrapper. -type Uint256 big.Int +func (Uint128) BitsLen() uint { return 128 } -func NewUint256(v *big.Int) *Uint256 { - return (*Uint256)(AsUnsigned(v, 256)) -} +// ToBigInt returns the value as a new big.Int. +func (x Uint128) ToBigInt() *big.Int { return uintBytesToBigInt(x.F[:]) } -func (*Uint256) BitsLen() uint { - return 256 -} +// Value is an alias for [Uint128.ToBigInt]. +func (x Uint128) Value() *big.Int { return x.ToBigInt() } -func (x *Uint256) Cmp(y *Uint256) (r int) { - return x.Value().Cmp(y.Value()) -} +func (x Uint128) String() string { return x.ToBigInt().String() } + +// Cmp compares x and y and returns -1, 0 or +1. +func (x Uint128) Cmp(y Uint128) int { return x.ToBigInt().Cmp(y.ToBigInt()) } // LoadFromCell implements tlb.Unmarshaler. -func (x *Uint256) LoadFromCell(loader *cell.Slice) error { - b := new(BigUint) - err := b.loadFromCell(x.BitsLen(), loader) +func (x *Uint128) LoadFromCell(loader *cell.Slice) error { return uintBytesFromCell(x.F[:], loader) } + +// ToCell implements tlb.Marshaller. +func (x Uint128) ToCell() (*cell.Cell, error) { return uintBytesToCell(x.F[:]) } + +// MarshalJSON implements the [encoding/json.Marshaler] interface. +func (x Uint128) MarshalJSON() ([]byte, error) { return uintBytesMarshalJSON(x.F[:]) } + +// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface. +func (x *Uint128) UnmarshalJSON(data []byte) error { + v, err := parseUintJSON(data, 128) if err != nil { - return fmt.Errorf("failed to load Uint256 from cell: %w", err) + return err } + copy(x.F[:], uintBytesFromBigInt(v, 128, 16)) - *x = Uint256(*b.Value) return nil } -// ToCell implements tlb.Marshaller. -func (x Uint256) ToCell() (*cell.Cell, error) { - b := BigUint{ - Bits: x.BitsLen(), - Value: (*big.Int)(&x), - } - return b.toCell(x.BitsLen()) +// Uint160 is a 160-bit unsigned integer (Tolk uint160, e.g. a 20-byte EVM +// address). See the fixed-width section note above. +type Uint160 struct { + F [20]byte } -func (x *Uint256) MarshalJSON() ([]byte, error) { - if x == nil { - return []byte("null"), nil - } - - // Canonical output: hexadecimal string. - // Avoids precision/scientific-notation issues in intermediate tooling. - v := x.Value() - if v.Sign() < 0 || v.BitLen() > int(x.BitsLen()) { - return nil, errors.New("failed to marshal Uint256: out of range") - } +// NewUint160 builds a Uint160 from a big.Int, masking the input to 160 bits. +func NewUint160(v *big.Int) *Uint160 { + var a [20]byte + copy(a[:], uintBytesFromBigInt(v, 160, 20)) - hex := v.Text(16) // lowercase - return []byte(`"0x` + hex + `"`), nil + return &Uint160{F: a} } -// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface. -func (x *Uint256) UnmarshalJSON(data []byte) error { - // Try JSON string first - var s string - if err := json.Unmarshal(data, &s); err == nil { - base := 10 - if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") { - base = 16 - s = s[2:] - } - b, ok := new(big.Int).SetString(s, base) - if !ok { - return fmt.Errorf("failed to parse Uint256 string %q", s) - } +func (Uint160) BitsLen() uint { return 160 } - if b.Sign() < 0 || b.BitLen() > int(x.BitsLen()) { - return errors.New("failed to unmarshal Uint256 from JSON: out of range") - } - *x = Uint256(*b) - return nil - } +// ToBigInt returns the value as a new big.Int. +func (x Uint160) ToBigInt() *big.Int { return uintBytesToBigInt(x.F[:]) } - // Fallback: plain JSON number - b := new(big.Int) - if err := b.UnmarshalJSON(data); err != nil { - return fmt.Errorf("failed to unmarshal Uint256 from JSON: %w", err) - } +// Value is an alias for [Uint160.ToBigInt]. +func (x Uint160) Value() *big.Int { return x.ToBigInt() } + +func (x Uint160) String() string { return x.ToBigInt().String() } + +// Cmp compares x and y and returns -1, 0 or +1. +func (x Uint160) Cmp(y Uint160) int { return x.ToBigInt().Cmp(y.ToBigInt()) } + +// LoadFromCell implements tlb.Unmarshaler. +func (x *Uint160) LoadFromCell(loader *cell.Slice) error { return uintBytesFromCell(x.F[:], loader) } + +// ToCell implements tlb.Marshaller. +func (x Uint160) ToCell() (*cell.Cell, error) { return uintBytesToCell(x.F[:]) } - if b.Sign() < 0 || b.BitLen() > int(x.BitsLen()) { - return errors.New("failed to unmarshal Uint256 from JSON: out of range") +// MarshalJSON implements the [encoding/json.Marshaler] interface. +func (x Uint160) MarshalJSON() ([]byte, error) { return uintBytesMarshalJSON(x.F[:]) } + +// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface. +func (x *Uint160) UnmarshalJSON(data []byte) error { + v, err := parseUintJSON(data, 160) + if err != nil { + return err } - *x = Uint256(*b) + copy(x.F[:], uintBytesFromBigInt(v, 160, 20)) + return nil } -func (x Uint256) Value() *big.Int { - return (*big.Int)(&x) +// Uint256 is a 256-bit unsigned integer (Tolk uint256, e.g. a merkle root or +// operation id). See the fixed-width section note above. +type Uint256 struct { + F [32]byte } -func (x Uint256) String() string { - return x.Value().String() +// NewUint256 builds a Uint256 from a big.Int, masking the input to 256 bits. +func NewUint256(v *big.Int) *Uint256 { + var a [32]byte + copy(a[:], uintBytesFromBigInt(v, 256, 32)) + + return &Uint256{F: a} +} + +func (Uint256) BitsLen() uint { return 256 } + +// ToBigInt returns the value as a new big.Int. +func (x Uint256) ToBigInt() *big.Int { return uintBytesToBigInt(x.F[:]) } + +// Value is an alias for [Uint256.ToBigInt]. +func (x Uint256) Value() *big.Int { return x.ToBigInt() } + +func (x Uint256) String() string { return x.ToBigInt().String() } + +// Cmp compares x and y and returns -1, 0 or +1. +func (x Uint256) Cmp(y Uint256) int { return x.ToBigInt().Cmp(y.ToBigInt()) } + +// LoadFromCell implements tlb.Unmarshaler. +func (x *Uint256) LoadFromCell(loader *cell.Slice) error { return uintBytesFromCell(x.F[:], loader) } + +// ToCell implements tlb.Marshaller. +func (x Uint256) ToCell() (*cell.Cell, error) { return uintBytesToCell(x.F[:]) } + +// MarshalJSON implements the [encoding/json.Marshaler] interface. +func (x Uint256) MarshalJSON() ([]byte, error) { return uintBytesMarshalJSON(x.F[:]) } + +// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface. +func (x *Uint256) UnmarshalJSON(data []byte) error { + v, err := parseUintJSON(data, 256) + if err != nil { + return err + } + copy(x.F[:], uintBytesFromBigInt(v, 256, 32)) + + return nil } diff --git a/cciplib/ton/tlbe/biguint_test.go b/cciplib/ton/tlbe/biguint_test.go index 15c3d0aab..46cd4ab89 100644 --- a/cciplib/ton/tlbe/biguint_test.go +++ b/cciplib/ton/tlbe/biguint_test.go @@ -1,6 +1,7 @@ package tlbe // tlb extras import ( + "encoding/json" "errors" "fmt" "math/big" @@ -17,10 +18,187 @@ type legacyValues struct { } type typedValues struct { + Address Uint160 `tlb:"."` + Root Uint256 `tlb:"."` +} + +// ptrTypedValues covers the pointer-field form still used by the shared struct +// definitions (e.g. *tlbe.Uint256), which must keep working unchanged. +type ptrTypedValues struct { Address *Uint160 `tlb:"."` Root *Uint256 `tlb:"."` } +// uint128SetHolder guards the generic path used by CursedSubjects: a struct +// field with a tlb:"." tag holding a *Dict[Uint128, struct{}]. +type uint128SetHolder struct { + Subjects *Dict[Uint128, struct{}] `tlb:"."` +} + +// bigFromHex builds a big.Int from a hex string (helper for readable test cases). +func bigFromHex(t *testing.T, s string) *big.Int { + t.Helper() + v, ok := new(big.Int).SetString(s, 16) + require.True(t, ok) + return v +} + +// TestUintWrappers_WireCompatWithRawStore locks in that the byte-based codec +// produces exactly the same cell as a raw fixed-width store, so on-chain data +// stays bit-identical to the previous BigUint/StoreBigInt path. +func TestUintWrappers_WireCompatWithRawStore(t *testing.T) { + v := new(big.Int).Add(new(big.Int).Lsh(big.NewInt(9), 200), big.NewInt(1234567)) + + cases := []struct { + name string + boxed any + raw *cell.Cell + }{ + {"uint128", *NewUint128(v), cell.BeginCell().MustStoreBigInt(AsUnsigned(v, 128), 128).EndCell()}, + {"uint160", *NewUint160(v), cell.BeginCell().MustStoreBigInt(AsUnsigned(v, 160), 160).EndCell()}, + {"uint256", *NewUint256(v), cell.BeginCell().MustStoreBigInt(AsUnsigned(v, 256), 256).EndCell()}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + c, err := tlb.ToCell(tc.boxed) + require.NoError(t, err) + require.Equal(t, tc.raw.Hash(), c.Hash(), + "boxed encoding must match the raw fixed-width big-endian store") + }) + } +} + +// TestUint128_Values checks the in-memory layout: big-endian bytes, so a bit set +// at position N lands in the expected word of F. +func TestUint128_Values(t *testing.T) { + // A value above the 64-bit range exercises multiple bytes. + v := new(big.Int).Add(new(big.Int).Lsh(big.NewInt(1), 100), big.NewInt(12345)) + + u := NewUint128(v) + require.Equal(t, v, u.ToBigInt()) + require.Equal(t, uint8(0x0), u.F[0]) // high bytes zero + require.Equal(t, uint8(0x10), u.F[3]) // 2^100 sets bit 4 of byte 3 + + // Zero and max round-trip through the bytes. + require.Equal(t, big.NewInt(0), NewUint128(big.NewInt(0)).ToBigInt()) + + maxVal := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 128), big.NewInt(1)) + require.Equal(t, maxVal, NewUint128(maxVal).ToBigInt()) +} + +// TestUintWrappers_AreComparable locks in the core property: the boxed fixed-width +// uints are value types with == semantics (unlike a big.Int alias), so they can +// be used as Go map / tlbe.Dict keys. +func TestUintWrappers_AreComparable(t *testing.T) { + v128 := new(big.Int).Lsh(big.NewInt(1), 100) + a128, b128 := *NewUint128(v128), *NewUint128(v128) + require.Equal(t, a128, b128, "equal Uint128 values must compare equal") + require.NotEqual(t, a128, *NewUint128(big.NewInt(1)), "different Uint128 values must compare unequal") + + v160 := new(big.Int).Lsh(big.NewInt(1), 159) + a160, b160 := *NewUint160(v160), *NewUint160(v160) + require.Equal(t, a160, b160, "equal Uint160 values must compare equal") + require.NotEqual(t, a160, *NewUint160(big.NewInt(1)), "different Uint160 values must compare unequal") + + v256 := new(big.Int).Lsh(big.NewInt(1), 255) + a256, b256 := *NewUint256(v256), *NewUint256(v256) + require.Equal(t, a256, b256, "equal Uint256 values must compare equal") + require.NotEqual(t, a256, *NewUint256(big.NewInt(1)), "different Uint256 values must compare unequal") +} + +// TestUintWrappers_DictKeyRoundTrip is the regression test for the pointer-key +// bug: boxed uint keys of every width must survive +// ToCell -> LoadDict -> NewDictFromDictionary (the previous *Uint256 key form +// mis-keyed and panicked in tonutils' reflection path). +func TestUintWrappers_DictKeyRoundTrip(t *testing.T) { + t.Run("uint128", func(t *testing.T) { + d := NewDict[Uint128, struct{}](map[Uint128]struct{}{ + *NewUint128(big.NewInt(5)): {}, + *NewUint128(new(big.Int).Lsh(big.NewInt(1), 127)): {}, + }) + + c, err := d.ToCell() + require.NoError(t, err) + + dict, err := c.BeginParse().LoadDict(128) + require.NoError(t, err) + + restored, err := NewDictFromDictionary[Uint128, struct{}](dict) + require.NoError(t, err) + require.Len(t, restored.AsMap(), 2) + + _, ok := restored.Get(*NewUint128(big.NewInt(5))) + require.True(t, ok) + _, ok = restored.Get(*NewUint128(new(big.Int).Lsh(big.NewInt(1), 127))) + require.True(t, ok) + }) + + t.Run("uint160", func(t *testing.T) { + d := NewDict[Uint160, bool](map[Uint160]bool{ + *NewUint160(big.NewInt(5)): true, + *NewUint160(new(big.Int).Lsh(big.NewInt(1), 159)): false, + *NewUint160(bigFromHex(t, "ffffffffffffffffffffffffffffffffffffffff")): true, + }) + + c, err := d.ToCell() + require.NoError(t, err) + + dict, err := c.BeginParse().LoadDict(160) + require.NoError(t, err) + + restored, err := NewDictFromDictionary[Uint160, bool](dict) + require.NoError(t, err) + require.Len(t, restored.AsMap(), 3) + + v, ok := restored.Get(*NewUint160(big.NewInt(5))) + require.True(t, ok) + require.True(t, v) + + v, ok = restored.Get(*NewUint160(new(big.Int).Lsh(big.NewInt(1), 159))) + require.True(t, ok) + require.False(t, v) + }) + + t.Run("uint256", func(t *testing.T) { + d := NewDict[Uint256, uint64](map[Uint256]uint64{ + *NewUint256(big.NewInt(7)): 42, + *NewUint256(new(big.Int).Lsh(big.NewInt(1), 255)): 99, + }) + + c, err := d.ToCell() + require.NoError(t, err) + + dict, err := c.BeginParse().LoadDict(256) + require.NoError(t, err) + + restored, err := NewDictFromDictionary[Uint256, uint64](dict) + require.NoError(t, err) + require.Len(t, restored.AsMap(), 2) + + v, ok := restored.Get(*NewUint256(new(big.Int).Lsh(big.NewInt(1), 255))) + require.True(t, ok) + require.Equal(t, uint64(99), v) + }) +} + +// TestUint128_DictStructFieldRoundTrip guards the generic dict-in-struct path +// used by CursedSubjects. +func TestUint128_DictStructFieldRoundTrip(t *testing.T) { + original := uint128SetHolder{ + Subjects: NewDict[Uint128, struct{}](map[Uint128]struct{}{ + *NewUint128(big.NewInt(3)): {}, + }), + } + + c, err := tlb.ToCell(original) + require.NoError(t, err) + + var decoded uint128SetHolder + require.NoError(t, tlb.LoadFromCell(&decoded, c.BeginParse())) + require.Len(t, decoded.Subjects.AsMap(), 1) +} + func TestUintWrappers_MaskSignBit(t *testing.T) { testCases := []struct { name string @@ -59,12 +237,19 @@ func TestUintWrappers_MaskSignBit(t *testing.T) { err = tlb.LoadFromCell(&typed, c.BeginParse()) require.NoError(t, err) - require.NotNil(t, typed.Address.Value(), "address value is nil") require.Equal(t, expectedAddr, typed.Address.Value(), "address mismatch") - - require.NotNil(t, typed.Root.Value(), "root value is nil") require.Equal(t, expectedRoot, typed.Root.Value(), "root mismatch") + // Pointer fields must decode too. + var ptrTyped ptrTypedValues + err = tlb.LoadFromCell(&ptrTyped, c.BeginParse()) + require.NoError(t, err) + + require.NotNil(t, ptrTyped.Address, "pointer address is nil") + require.Equal(t, expectedAddr, ptrTyped.Address.Value(), "pointer address mismatch") + require.NotNil(t, ptrTyped.Root, "pointer root is nil") + require.Equal(t, expectedRoot, ptrTyped.Root.Value(), "pointer root mismatch") + // Testing legacy big.Int loading - should interpret as signed integers var legacy legacyValues err = tlb.LoadFromCell(&legacy, c.BeginParse()) @@ -81,8 +266,8 @@ func TestUintWrappers_RoundTrip(t *testing.T) { root := leadingBytes(32, 0x7f) original := typedValues{ - Address: NewUint160(new(big.Int).SetBytes(addr)), - Root: NewUint256(new(big.Int).SetBytes(root)), + Address: *NewUint160(new(big.Int).SetBytes(addr)), + Root: *NewUint256(new(big.Int).SetBytes(root)), } cellValue, err := tlb.ToCell(original) @@ -92,11 +277,9 @@ func TestUintWrappers_RoundTrip(t *testing.T) { err = tlb.LoadFromCell(&decoded, cellValue.BeginParse()) require.NoError(t, err) - require.NotNil(t, decoded.Address.Value(), "address value is nil after roundtrip") require.Equal(t, new(big.Int).SetBytes(addr), decoded.Address.Value(), "address mismatch after roundtrip") - - require.NotNil(t, decoded.Root.Value(), "root value is nil after roundtrip") require.Equal(t, new(big.Int).SetBytes(root), decoded.Root.Value(), "root mismatch after roundtrip") + require.Equal(t, original, decoded, "value types must be comparable with ==") var legacy legacyValues err = tlb.LoadFromCell(&legacy, cellValue.BeginParse()) @@ -133,3 +316,74 @@ func encodeFixed(addr, root []byte) (*cell.Cell, error) { return builder.EndCell(), nil } + +// TestUintWrappers_CellRoundTripAndWidth checks each type's cell width and value. +func TestUintWrappers_CellRoundTripAndWidth(t *testing.T) { + testCases := []struct { + name string + build func(*big.Int) (any, uint) + }{ + {"uint128", func(v *big.Int) (any, uint) { return *NewUint128(v), 128 }}, + {"uint160", func(v *big.Int) (any, uint) { return *NewUint160(v), 160 }}, + {"uint256", func(v *big.Int) (any, uint) { return *NewUint256(v), 256 }}, + } + + value := new(big.Int).Add(new(big.Int).Lsh(big.NewInt(3), 120), big.NewInt(7)) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + original, bits := tc.build(value) + + c, err := tlb.ToCell(original) + require.NoError(t, err) + require.Equal(t, bits, c.BitsSize()) + + switch x := original.(type) { + case Uint128: + var decoded Uint128 + require.NoError(t, decoded.LoadFromCell(c.BeginParse())) + require.Equal(t, x, decoded) + require.Equal(t, x.ToBigInt(), decoded.ToBigInt()) + case Uint160: + var decoded Uint160 + require.NoError(t, decoded.LoadFromCell(c.BeginParse())) + require.Equal(t, x, decoded) + case Uint256: + var decoded Uint256 + require.NoError(t, decoded.LoadFromCell(c.BeginParse())) + require.Equal(t, x, decoded) + } + }) + } +} + +// TestUintWrappers_JSONHexRoundTrip checks canonical hex JSON for all three types. +func TestUintWrappers_JSONHexRoundTrip(t *testing.T) { + for _, jsonCase := range []struct { + value any + want string + }{ + {*NewUint128(big.NewInt(255)), `"0xff"`}, + {*NewUint160(big.NewInt(255)), `"0xff"`}, + {*NewUint256(big.NewInt(255)), `"0xff"`}, + } { + payload, err := json.Marshal(jsonCase.value) + require.NoError(t, err) + require.JSONEq(t, jsonCase.want, string(payload)) + + switch x := jsonCase.value.(type) { + case Uint128: + var d Uint128 + require.NoError(t, json.Unmarshal(payload, &d)) + require.Equal(t, x, d) + case Uint160: + var d Uint160 + require.NoError(t, json.Unmarshal(payload, &d)) + require.Equal(t, x, d) + case Uint256: + var d Uint256 + require.NoError(t, json.Unmarshal(payload, &d)) + require.Equal(t, x, d) + } + } +} diff --git a/cmd/chainlink-ton-extras/lock.nix b/cmd/chainlink-ton-extras/lock.nix index ac6444869..c413761db 100644 --- a/cmd/chainlink-ton-extras/lock.nix +++ b/cmd/chainlink-ton-extras/lock.nix @@ -1,5 +1,5 @@ # Notice: `pkgs.lib.fakeHash` can be used as a placeholder, # but `lock-nix-tidy` will only replace actual hashes. {pkgs}: { - chainlink-ton-extras = "sha256-lHYNeIUSxyJyHgoJcd/VjwLOynH2AP5Fp8nK4YpKhhU="; + chainlink-ton-extras = "sha256-CvAHD2AeJq51erMWhXj+u2FHFBIyREuGX7LjGbQagXk="; } diff --git a/cmd/chainlink-ton/lock.nix b/cmd/chainlink-ton/lock.nix index ee7495eb3..3bb66eba0 100644 --- a/cmd/chainlink-ton/lock.nix +++ b/cmd/chainlink-ton/lock.nix @@ -1,5 +1,5 @@ # Notice: `pkgs.lib.fakeHash` can be used as a placeholder, # but `lock-nix-tidy` will only replace actual hashes. {pkgs}: { - chainlink-ton = "sha256-lHYNeIUSxyJyHgoJcd/VjwLOynH2AP5Fp8nK4YpKhhU="; + chainlink-ton = "sha256-CvAHD2AeJq51erMWhXj+u2FHFBIyREuGX7LjGbQagXk="; } diff --git a/contracts/contracts/ccip/ccipsend_executor/contract.tolk b/contracts/contracts/ccip/ccipsend_executor/contract.tolk index debe12cca..860aac86b 100644 --- a/contracts/contracts/ccip/ccipsend_executor/contract.tolk +++ b/contracts/contracts/ccip/ccipsend_executor/contract.tolk @@ -81,6 +81,40 @@ fun onInternalMessage(in: InMessage) { assert(this.state.load().tokenPool == in.senderAddress, CCIPSendExecutor_Error.Unauthorized); this.onConfirmWithdraw(msg); } + // Structured pool failures can arrive either before custody withdrawal + // or while the withdrawal/finalization phase is in progress. + TokenPool_LockOrBurnFailure => { + val st = lazy CCIPSendExecutor_Data.fromCell(contract.getData()); + if (st.state is Cell) { + var this = CCIPSendExecutor.load(); + assert(this.state.load().tokenPool == in.senderAddress, CCIPSendExecutor_Error.Unauthorized); + this.exitWithError(msg.errorCode as uint256); + } else { + var this = CCIPSendExecutor.load(); + assert(this.state.load().tokenPool == in.senderAddress, CCIPSendExecutor_Error.Unauthorized); + this.exitWithError(msg.errorCode as uint256); + } + } + // Router policy rejection, a pre-admission pool bounce, or a bounced + // Router-wallet withdrawal is reported by the configured Router. + Router_TokenPoolLockOrBurnFailed => { + val st = lazy CCIPSendExecutor_Data.fromCell(contract.getData()); + if (st.state is Cell) { + var this = CCIPSendExecutor.load(); + val state = lazy this.state.load(); + val addresses = lazy this.addresses.load(); + assert(addresses.router == in.senderAddress, CCIPSendExecutor_Error.Unauthorized); + assert(state.tokenPool == msg.tokenPool, CCIPSendExecutor_Error.Unauthorized); + this.exitWithError(CCIPSendExecutor_Error.TokenPoolBounce as uint256); + } else { + var this = CCIPSendExecutor.load(); + val state = lazy this.state.load(); + val addresses = lazy this.addresses.load(); + assert(addresses.router == in.senderAddress, CCIPSendExecutor_Error.Unauthorized); + assert(state.tokenPool == msg.tokenPool, CCIPSendExecutor_Error.Unauthorized); + this.exitWithError(CCIPSendExecutor_Error.TokenPoolBounce as uint256); + } + } else => { assert (in.body.isEmpty()) throw 0xFFFF; }, diff --git a/contracts/contracts/ccip/ccipsend_executor/errors.tolk b/contracts/contracts/ccip/ccipsend_executor/errors.tolk index bf0e3dd02..1c7e76d21 100644 --- a/contracts/contracts/ccip/ccipsend_executor/errors.tolk +++ b/contracts/contracts/ccip/ccipsend_executor/errors.tolk @@ -8,4 +8,5 @@ enum CCIPSendExecutor_Error { InsufficientFee FeeQuoterBounce TokenNotEnabled + TokenPoolBounce } diff --git a/contracts/contracts/ccip/ccipsend_executor/messages.tolk b/contracts/contracts/ccip/ccipsend_executor/messages.tolk index 1e5f6a530..6214a40c8 100644 --- a/contracts/contracts/ccip/ccipsend_executor/messages.tolk +++ b/contracts/contracts/ccip/ccipsend_executor/messages.tolk @@ -1,5 +1,6 @@ import "../../lib/jetton/messages" +import "../router/messages" import "../onramp/messages" import "../fee_quoter/messages" import "../token_admin_registry_entry/messages" @@ -14,7 +15,9 @@ type CCIPSendExecutor_InMessage = | TokenAdminRegistryEntry_ReturnTokenInfo | ResponseWalletAddress | TokenPool_LockOrBurnWithdraw - | TokenPool_LockOrBurnFinished; + | TokenPool_LockOrBurnFinished + | TokenPool_LockOrBurnFailure + | Router_TokenPoolLockOrBurnFailed; type FeeQuoter_MessageValidated_Any = FeeQuoter_MessageValidated type FeeQuoter_MessageValidationFailed_Any = FeeQuoter_MessageValidationFailed diff --git a/contracts/contracts/ccip/offramp/contract.tolk b/contracts/contracts/ccip/offramp/contract.tolk index d1eaa0fc0..d4072731e 100644 --- a/contracts/contracts/ccip/offramp/contract.tolk +++ b/contracts/contracts/ccip/offramp/contract.tolk @@ -117,6 +117,12 @@ fun onInternalMessage(in:InMessage) { assertSenderIsTheRouter(st, msg.execId.getSourceChainSelector(), in.senderAddress); onBouncedCCIPReceive(msg); } + // Sender must be router + Router_TokenPoolReleaseOrMintFailed => { + val st = lazy Storage.load(); + assertSenderIsTheRouter(st, msg.sourceChainSelector, in.senderAddress); + onTokenPoolReleaseOrMintFailed(msg); + } // Sender must be ReceiveExecutor OffRamp_NotifySuccess => { val st = lazy Storage.load(); @@ -177,8 +183,8 @@ fun onBouncedMessage(in: InMessageBounced) { Router_RouteMessage => { onBouncedRouterRouteMessage(originalMessage, in.senderAddress) }, - TokenPool_ReleaseOrMint => { - onBouncedTokenPoolReleaseOrMint(originalMessage, rich.exitCode) + Router_RelayReleaseOrMint => { + onBouncedRouterRelayReleaseOrMint(originalMessage, rich.exitCode) }, } } @@ -236,11 +242,11 @@ fun onBouncedDeployableInitialize(msg: Deployable_Initialize, senderAddress: add }); } -// Handles bounced TokenPool_ReleaseOrMint messages. -// This can be the result of the rate limit being exceeded or the pool being misconfigured. -// Sends a OffRamp_ReleaseOrMintBounced message to the ReceiveExecutor to mark the execution as failed. -fun onBouncedTokenPoolReleaseOrMint(msg: TokenPool_ReleaseOrMint, exitCode: int32) { - mintOrReleaseFailed(msg.replyTo!, msg.queryId, ReleaseOrMintBounced { exitCode }); +// Handles a bounced Router_RelayReleaseOrMint: the Router rejected the relay +// (e.g. this OffRamp is no longer registered, or it reverted). Fails the +// executor through the usual rail. +fun onBouncedRouterRelayReleaseOrMint(msg: Router_RelayReleaseOrMint, exitCode: int32) { + mintOrReleaseFailed(msg.replyTo, msg.queryID, ReleaseOrMintBounced { exitCode }); } // Handles bounced Router_RouteMessage messages. This should never happen; if @@ -507,23 +513,35 @@ fun mintOrReleaseFailed(receiveExecutor: address, queryId: uint64, reason: Relea receiveBounced.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } +// Fails the executor after the Router reported a release/mint failure (lane +// cursed, or the pool bounced the relay). +fun onTokenPoolReleaseOrMintFailed(msg: Router_TokenPoolReleaseOrMintFailed) { + mintOrReleaseFailed(msg.replyTo, msg.queryID, ReleaseOrMintBounced { exitCode: msg.exitCode }); +} + +// Relays a release/mint request through the Router, the single access point of +// every pool (mirrors EVM `_onlyOffRamp`). Pool replies go straight to +// `sendExecutor`; relay rejections and pool bounces come back to us as +// Router_TokenPoolReleaseOrMintFailed. fun onReleaseOrMint(st: Storage, sendExecutor: address, msg: OffRamp_ReleaseOrMint) { if (msg.destGasAmount < MIN_TT_GASLIMIT) { return mintOrReleaseFailed(getReceiverExecutorDeployAddress(st, msg.execId).calculateAddress(), msg.queryId, NotEnoughDestGasAmountForTokenTransfer {}); } - val releaseOrMintMsg = createMessage({ + val relayMsg = createMessage({ bounce: BounceMode.RichBounce, value: 0, - dest: msg.tokenPool, - body: TokenPool_ReleaseOrMint { - queryId: msg.queryId, + dest: getRouterFromSelector(st, msg.execId.getSourceChainSelector()), + body: Router_RelayReleaseOrMint { + queryID: msg.queryId, + sourceChainSelector: msg.execId.getSourceChainSelector(), + tokenPool: msg.tokenPool, request: msg.request, requestedFinalityConfig: msg.requestedFinalityConfig, replyTo: sendExecutor, } }); - releaseOrMintMsg.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + relayMsg.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } // Sets a new OCR3 configuration for the contract. Only the contract owner can call this function. diff --git a/contracts/contracts/ccip/offramp/messages.tolk b/contracts/contracts/ccip/offramp/messages.tolk index adc17f6a6..7f0224a09 100644 --- a/contracts/contracts/ccip/offramp/messages.tolk +++ b/contracts/contracts/ccip/offramp/messages.tolk @@ -27,6 +27,7 @@ type OffRamp_InMessage = | OffRamp_UpdateSourceChainConfigs | OffRamp_CCIPReceiveConfirm | OffRamp_CCIPReceiveBounced + | Router_TokenPoolReleaseOrMintFailed | OffRamp_NotifyFailure | OffRamp_NotifySuccess | OffRamp_UpdateCursedSubjects @@ -38,7 +39,7 @@ type OffRamp_InMessage = type OffRamp_InMessageBounced = | ReceiveExecutor_InitExecute | Deployable_Initialize - | TokenPool_ReleaseOrMint + | Router_RelayReleaseOrMint | Router_RouteMessage; struct (0x9d431905) OffRamp_Commit { diff --git a/contracts/contracts/ccip/pools/TokenPool.tolk b/contracts/contracts/ccip/pools/TokenPool.tolk index 0ec3d63da..3605e0ff3 100644 --- a/contracts/contracts/ccip/pools/TokenPool.tolk +++ b/contracts/contracts/ccip/pools/TokenPool.tolk @@ -52,16 +52,8 @@ get fun isSupportedChain(remoteChainSelector: uint64): bool { throw 0xFFFF; } -get fun onRamp(remoteChainSelector: uint64): address? { - throw 0xFFFF; -} - -get fun offRamp(remoteChainSelector: uint64): address? { - throw 0xFFFF; -} - /// Matches Go binding GetRMNProxy (Name: "getRMNProxy"). -get fun getRMNProxy(): address { +get fun getRMNProxy(): address? { throw 0xFFFF; } @@ -133,11 +125,6 @@ get fun getAdminConfig(): TokenPool_AdminConfig { throw 0xFFFF; } -/// Matches Go binding GetMirroredPolicy (Name: "getMirroredPolicy"). -get fun getMirroredPolicy(): TokenPool_MirroredPolicy { - throw 0xFFFF; -} - /// Matches Go binding GetRemoteChainConfig (Name: "getRemoteChainConfig"). get fun getRemoteChainConfig(remoteChainSelector: uint64): TokenPool_RemoteChainConfig? { throw 0xFFFF; diff --git a/contracts/contracts/ccip/pools/burn_mint/contract.tolk b/contracts/contracts/ccip/pools/burn_mint/contract.tolk index 69752965b..c00839dc0 100644 --- a/contracts/contracts/ccip/pools/burn_mint/contract.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/contract.tolk @@ -36,6 +36,11 @@ contract BurnMintTokenPool { incomingMessages: | TokenPool_InMessage | BurnMintTokenPool_InMessage + + forceAbiExport: ( + BurnMintTokenPool_BurnContext, + BurnMintTokenPool_MintContext + ) } const CONTRACT_VERSION = "0.1.0".literalSlice(); @@ -251,7 +256,7 @@ fun onClaimMinterAdmin( /// The minter relays the burn continuation context (the `BurnMintTokenPool_BurnContext` /// embedded by this pool in `CCT_AskToBurn.forwardPayload`) back to this pool, which then /// finalizes the lock/burn. Context-less excess signals (e.g. from the mint/transfer paths) -/// are ignored. Mint still finalizes via OffRampAccount. +/// are ignored. fun onCCTReturnExcessesBack( mutate st: Storage, sender: address, @@ -270,8 +275,6 @@ fun onCCTReturnExcessesBack( val context = lazy BurnMintTokenPool_BurnContext.fromCell(msg.forwardPayload!); val operation = lazy context.forwardPayload.load(); - // TODO: is this check necessary? - assert(context.wallet == pool.data.jettonClient().walletAddress(), BurnMintTokenPool_Error.UnexpectedBurnBounce); pool.onLockOrBurnTransferFinalize(operation); st.poolData = pool.data.toCell(); @@ -430,8 +433,6 @@ fun loadPool(st: Storage): TokenPool { data: st.poolData.load(), context: st, hooks: TokenPool_Hooks { - ensureOutboundAccess: null, - ensureInboundAccess: null, ensureNotCursed: null, onLockOrBurn: null, validateLockOrBurn: null, @@ -471,22 +472,12 @@ get fun isSupportedChain(remoteChainSelector: uint64): bool { return Storage.load().poolData.load().remoteChainConfigs.get(remoteChainSelector).isFound; } -get fun onRamp(remoteChainSelector: uint64): address? { - val entry = Storage.load().poolData.load().mirroredPolicy.load().onRamps.get(remoteChainSelector); - return entry.isFound ? entry.loadValue() : null; -} - -get fun offRamp(remoteChainSelector: uint64): address? { - val entry = Storage.load().poolData.load().mirroredPolicy.load().offRamps.get(remoteChainSelector); - return entry.isFound ? entry.loadValue() : null; -} - -get fun getRMNProxy(): address { +get fun getRMNProxy(): address? { return Storage.load().poolData.load().adminConfig.load().rmnProxy; } get fun verifyNotCursed(subject: uint128): bool { - return !Storage.load().poolData.load().mirroredPolicy.load().cursedSubjects.isCursed(subject); + return !Storage.load().poolData.load().localPolicy.load().cursedSubjects.isCursed(subject); } get fun owner(): address { diff --git a/contracts/contracts/ccip/pools/burn_mint/storage.tolk b/contracts/contracts/ccip/pools/burn_mint/storage.tolk index 17d4b4aa5..f3e0ca061 100644 --- a/contracts/contracts/ccip/pools/burn_mint/storage.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/storage.tolk @@ -41,9 +41,7 @@ fun Storage.init(config: BurnMintTokenPool_InitConfig): Storage { advancedPoolHooks: createAddressNone() as address, deployableCode: config.deployableCode, }.toCell(), - mirroredPolicy: TokenPool_MirroredPolicy { - onRamps: createEmptyMap(), - offRamps: createEmptyMap(), + localPolicy: TokenPool_LocalPolicy { cursedSubjects: CursedSubjects { data: createEmptyMap(), }, @@ -54,4 +52,4 @@ fun Storage.init(config: BurnMintTokenPool_InitConfig): Storage { }.toCell(), offRampAccountCode: config.offRampAccountCode, }; -} \ No newline at end of file +} diff --git a/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk b/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk index 2f2ec8ba8..7b9df8091 100644 --- a/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk +++ b/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk @@ -49,7 +49,7 @@ struct TokenPool { struct TokenPool_AdminConfig { ownable: Cell; - rmnProxy: address; + rmnProxy: address?; dynamicConfig: Cell; /// The pool's Jetton identity (master + wallet code). Single source of truth used to /// derive and authenticate the pool's own wallet. @@ -70,7 +70,7 @@ struct TokenPool_AdminConfig { struct TokenPool_Data { adminConfig: Cell; - mirroredPolicy: Cell; + localPolicy: Cell; tokenDecimals: uint8; remoteChainConfigs: map = []; tokenTransferFeeConfigs: map = []; @@ -90,20 +90,22 @@ fun TokenPool_Data.storeAsContractData(self) { /// Optional extension points wired by concrete pools (e.g. lock/release) to customize the base /// TokenPool behavior. Each field is the implementation of the corresponding hook, or `null` to -/// use the base behavior. Pools share a generic `T` as the first arg, their per-call `context` (nullable). +/// use the base behavior. Pools share a generic `T` = their per-call `context`, which flows +/// through every hook (`T?` when a hook may be invoked with no context, e.g. getters). +/// +/// There are three hook families, mirroring the three flows: +/// - **Guards**: curse checks, invoked from the base admission path. +/// - **Operation hooks**: entry + validation + (pre/post)flight for the LockOrBurn and +/// ReleaseOrMint flows. Hooks returning a `TokenPool_*Prepared` may also signal `wait` to defer +/// execution to the on-chain (async) `advancedPoolHooks` contract. +/// - **Continuation hooks**: run after Jetton custody is settled, letting the pool track binding +/// side effects (e.g. lock/release accrues fees only on confirmed deposit). +/// +/// Setting an operation hook replaces the base flow for that phase, so the override owns every +/// check the base path would have performed (e.g., `validateLockOrBurn` / `validateReleaseOrMint`). +/// +/// The fee-withdrawal hook `onWithdrawFeeTokens` is a fourth, standalone extension point. struct TokenPool_Hooks { - /// Outbound (LockOrBurn) access guard. - /// @param sender The msg.sender requesting the outbound transfer. - /// @param remoteChainSelector The destination lane; must be supported. - /// @dev Base behavior: requires `sender` to be the configured OnRamp for the lane. - ensureOutboundAccess: ((T?, address, uint64) -> void)?; - - /// Inbound (ReleaseOrMint) access guard. - /// @param sender The msg.sender requesting the inbound transfer. - /// @param remoteChainSelector The source lane; must be supported. - /// @dev Base behavior: requires `sender` to be the configured OffRamp for the lane. - ensureInboundAccess: ((T?, address, uint64) -> void)?; - /// RMN curse guard for a remote lane. /// @param remoteChainSelector The remote lane to check for a curse. /// @dev Base behavior: reverts if the lane's cursed subjects mark it cursed by RMN. @@ -251,7 +253,7 @@ fun TokenPool.getTokenDecimals(self): uint8 { } @inline -fun TokenPool.getRmnProxy(self): address { +fun TokenPool.getRmnProxy(self): address? { val ac = lazy self.data.adminConfig.load(); return ac.rmnProxy; } @@ -275,24 +277,14 @@ fun TokenPool.getAllowedFinalityConfig(self): uint32 { /// Gets the advanced pool hook contract address used by this pool. @inline fun TokenPool.getAdvancedPoolHooks(self): address? { - return self.data.adminConfig.load().advancedPoolHooks; -} - -@inline -fun TokenPool.getOnRamp(self, remoteChainSelector: uint64): address? { - val entry = self.data.mirroredPolicy.load().onRamps.get(remoteChainSelector); - return entry.isFound ? entry.loadValue() : null; -} - -@inline -fun TokenPool.getOffRamp(self, remoteChainSelector: uint64): address? { - val entry = self.data.mirroredPolicy.load().offRamps.get(remoteChainSelector); - return entry.isFound ? entry.loadValue() : null; + val adminConfig = lazy self.data.adminConfig.load(); + return adminConfig.advancedPoolHooks; } @inline fun TokenPool.verifyNotCursed(self, subject: uint128): bool { - return !self.data.mirroredPolicy.load().cursedSubjects.isCursed(subject); + val localPolicy = lazy self.data.localPolicy.load(); + return !localPolicy.cursedSubjects.isCursed(subject); } /// Checks whether a remote chain is supported in the token pool. @@ -361,6 +353,7 @@ fun TokenPool.setDynamicConfig( assert(!isZeroAddress(router), TokenPool_Error.ZeroAddressInvalid); // feeAdmin: `null` means owner-only; the zero address is never a valid fee admin. assert(feeAdmin == null || !isZeroAddress(feeAdmin!), TokenPool_Error.ZeroAddressInvalid); + adminConfig.dynamicConfig = TokenPool_DynamicConfig { router, rateLimitAdmin, @@ -465,7 +458,7 @@ fun TokenPool.setAllowedDepositNamespaces( mutate self, sender: address, queryId: uint64, - allowedDepositNamespaces: map, + allowedDepositNamespaces: map, ) { var adminConfig = self.data.adminConfig.load(); adminConfig.ownable.load().requireOwner(sender); @@ -631,62 +624,20 @@ fun TokenPool.applyChainUpdates( reply.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } -fun TokenPool.applyRampAccessUpdates( - mutate self, - sender: address, - queryId: uint64, - updates: SnakedCell, -) { - val adminConfig = self.data.adminConfig.load(); - adminConfig.ownable.load().requireOwner(sender); - - var mirroredPolicy = self.data.mirroredPolicy.load(); - var iter = updates.iter(); - while (!iter.empty()) { - val update = iter.next(); - - if (update.onRamp != null) { - mirroredPolicy.onRamps.set(update.remoteChainSelector, update.onRamp!); - } else { - mirroredPolicy.onRamps.delete(update.remoteChainSelector); - } - - if (update.offRamp != null) { - mirroredPolicy.offRamps.set(update.remoteChainSelector, update.offRamp!); - } else { - mirroredPolicy.offRamps.delete(update.remoteChainSelector); - } - - emit(TOPIC_TOKEN_POOL_RAMP_ACCESS_UPDATED, TokenPool_RampAccessUpdated { - remoteChainSelector: update.remoteChainSelector, - onRamp: update.onRamp, - offRamp: update.offRamp, - }); - } - - self.data.mirroredPolicy = mirroredPolicy.toCell(); - - val reply = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: sender, - body: TokenPool_RampAccessUpdatesApplied { queryId }, - }); - reply.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); -} - +// TODO: make rmnProxy immutable (same as EVM) fun TokenPool.setRMNProxy( mutate self, sender: address, queryId: uint64, - rmnProxy: address + rmnProxy: address? ) { var adminConfig = self.data.adminConfig.load(); adminConfig.ownable.load().requireOwner(sender); // EVM makes `rmnProxy` immutable (constructor-checked); TON makes it mutable here, so the // zero-address guard must live on the setter. See AUDIT_REPORT.md L-11. - assert(!isZeroAddress(rmnProxy), TokenPool_Error.ZeroAddressInvalid); + // `null` is a legal value: it unsets the proxy; the zero address is never valid. + assert(rmnProxy == null || !isZeroAddress(rmnProxy!), TokenPool_Error.ZeroAddressInvalid); adminConfig.rmnProxy = rmnProxy; self.data.adminConfig = adminConfig.toCell(); @@ -708,9 +659,9 @@ fun TokenPool.setCursedSubjects( ) { self.onlyOwnerOrRMNProxy(sender); - var mirroredPolicy = self.data.mirroredPolicy.load(); - mirroredPolicy.cursedSubjects = cursedSubjects; - self.data.mirroredPolicy = mirroredPolicy.toCell(); + var localPolicy = self.data.localPolicy.load(); + localPolicy.cursedSubjects = cursedSubjects; + self.data.localPolicy = localPolicy.toCell(); val reply = createMessage({ bounce: BounceMode.NoBounce, @@ -789,9 +740,6 @@ fun TokenPool.onInternalMessage(mutate self, msgSender: address, msgValue: co TokenPool_ApplyTokenTransferFeeConfigUpdates => { self.applyTokenTransferFeeConfigUpdates(msgSender, msg.queryId, msg.updates, msg.disableChainSelectors); } - TokenPool_UpdateRampAccess => { - self.applyRampAccessUpdates(msgSender, msg.queryId, msg.updates); - } TokenPool_SetRMNProxy => { self.setRMNProxy(msgSender, msg.queryId, msg.rmnProxy); } @@ -832,6 +780,7 @@ fun TokenPool.onPreflightCheckFinished( sender: address, msg: TokenPool_PreflightCheckFinished, ): void { + self.onlyAdvancedPoolHooks(sender); val fwdp = msg.forwardPayload.load(); val originalSender = fwdp.originalSender; val requestMsg = lazy fwdp.requestMsg.load(); @@ -848,6 +797,7 @@ fun TokenPool.onPreflightCheckFailed( sender: address, msg: TokenPool_PreflightCheckFailed, ): void { + self.onlyAdvancedPoolHooks(sender); val fwdp = msg.forwardPayload.load(); // Refund outbound rate limit that was consumed before the hook check. @@ -864,6 +814,7 @@ fun TokenPool.onPostflightCheckFinished( sender: address, msg: TokenPool_PostflightCheckFinished, ): void { + self.onlyAdvancedPoolHooks(sender); val fwdp = msg.forwardPayload.load(); val originalSender = fwdp.originalSender; val requestMsg = lazy fwdp.requestMsg.load(); @@ -880,6 +831,7 @@ fun TokenPool.onPostflightCheckFailed( sender: address, msg: TokenPool_PostflightCheckFailed, ): void { + self.onlyAdvancedPoolHooks(sender); val fwdp = msg.forwardPayload.load(); // Refund inbound rate limit that was consumed before the hook check. @@ -992,8 +944,7 @@ fun TokenPool.refundLockOrBurnRateLimit( forwardPayload: TokenPool_LockOrBurnForwardPayload, ): void { val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); + val details = lazy requestMsg.request.load().transfer.details.load(); val prepared = lazy forwardPayload.prepared.load(); if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { @@ -1012,8 +963,7 @@ fun TokenPool.refundReleaseOrMintRateLimit( forwardPayload: TokenPool_ReleaseOrMintForwardPayload, ): void { val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); + val details = lazy requestMsg.request.load().transfer.details.load(); val prepared = lazy forwardPayload.prepared.load(); if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { @@ -1089,7 +1039,8 @@ fun TokenPool.getFee( requestedFinalityConfig: uint32, _tokenArgs: cell?, ): (coins, uint32, uint32, uint16, bool) { - TokenPool.ensureRequestedFinalityAllowed(requestedFinalityConfig, self.data.adminConfig.load().allowedFinalityConfig); + val adminConfig = lazy self.data.adminConfig.load(); + TokenPool.ensureRequestedFinalityAllowed(requestedFinalityConfig, adminConfig.allowedFinalityConfig); val entry = self.data.tokenTransferFeeConfigs.get(destChainSelector); if (!entry.isFound) { @@ -1244,7 +1195,8 @@ fun TokenPool.validateGetCCVsRequest( remoteChainSelector: uint64, requestedFinalityConfig: uint32, ): void { - TokenPool.ensureRequestedFinalityAllowed(requestedFinalityConfig, self.data.adminConfig.load().allowedFinalityConfig); + val adminConfig = lazy self.data.adminConfig.load(); + TokenPool.ensureRequestedFinalityAllowed(requestedFinalityConfig, adminConfig.allowedFinalityConfig); assert(self.isSupportedToken(localToken), TokenPool_Error.InvalidToken); self.requireSupportedChain(remoteChainSelector); } @@ -1266,7 +1218,8 @@ fun TokenPool.onGetCCVs( fees, }; - val hooks = self.data.adminConfig.load().advancedPoolHooks; + val adminConfig = lazy self.data.adminConfig.load(); + val hooks = adminConfig.advancedPoolHooks; if (hooks != null) { val ccvAmount = self.getCCVAmount(msg.remoteChainSelector, msg.amount, msg.requestedFinalityConfig, msg.direction, msg.extraData); val hooksMsg = createMessage({ @@ -1384,7 +1337,8 @@ fun TokenPool.onQueryCCVsReply( sender: address, msg: TokenPool_QueryCCVsReply, ): void { - val hooks = self.data.adminConfig.load().advancedPoolHooks; + val adminConfig = lazy self.data.adminConfig.load(); + val hooks = adminConfig.advancedPoolHooks; assert(hooks != null && hooks! == sender, TokenPool_Error.Unauthorized); assert(msg.replyPayload != null, TokenPool_Error.MissingForwardPayload); @@ -1410,14 +1364,15 @@ fun TokenPool.onGetCCVsBounced( /// Validates the lock or burn input for correctness on /// - token to be locked or burned /// - RMN curse status -/// - if the sender is a valid onRamp +/// - if the sender is the configured Router /// - rate limiting for either default or FTF transfer messages. /// - preflight checks hooks (if enabled) /// /// @param sender The address of the original sender of the lock or burn request. /// @param msg The full LockOrBurn message containing all request fields. /// -/// @dev This function should always be called before executing a lock or burn. +/// @dev This function should always be called before executing a lock or burn. Not doing so would +/// allow for various exploits. fun TokenPool.validateLockOrBurn( mutate self, sender: address, @@ -1431,19 +1386,20 @@ fun TokenPool.validateLockOrBurn( return (wait, prepared); } - val request = msg.request.load(); + val request = lazy msg.request.load(); val details: TokenPool_TransferDetails, coins> = lazy request.transfer.details.load(); assert(self.isSupportedToken(details.localToken), TokenPool_Error.InvalidToken); self.requireSupportedChain(details.remoteChainSelector); self.ensureNotCursed(details.remoteChainSelector); - self.ensureOutboundAccess(sender, details.remoteChainSelector); + self.onlyRouter(sender); val feeAmount = self.getFeeAmount(request.transfer, msg.requestedFinalityConfig); val destTokenAmount = details.amount - feeAmount; // Consume rate limit BEFORE hook check — _consume throws BucketOverfilled // designed to bounce to the original sender, not to the hooks contract. if (msg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { - TokenPool.ensureRequestedFinalityAllowed(msg.requestedFinalityConfig, self.data.adminConfig.load().allowedFinalityConfig); + val adminConfig = lazy self.data.adminConfig.load(); + TokenPool.ensureRequestedFinalityAllowed(msg.requestedFinalityConfig, adminConfig.allowedFinalityConfig); self.consumeFastFinalityOutboundRateLimit(details.remoteChainSelector, destTokenAmount); } else { self.consumeOutboundRateLimit(details.remoteChainSelector, destTokenAmount); @@ -1600,8 +1556,8 @@ fun TokenPool.assertAuthorizedDepositInitiator( initiator: address, originalSender: address, ): void { - val adminConfig = self.data.adminConfig.load(); - val dynamicConfig = adminConfig.dynamicConfig.load(); + val adminConfig = lazy self.data.adminConfig.load(); + val dynamicConfig = lazy adminConfig.dynamicConfig.load(); // The Router itself is always a trusted source (standard Router -> pool withdraw path). if (initiator == dynamicConfig.router) { @@ -1639,7 +1595,8 @@ fun TokenPool.deriveDepositAccount( namespace: Namespace, user: address, ): AutoDeployAddress { - val code = self.data.adminConfig.load().deployableCode; + val adminConfig = lazy self.data.adminConfig.load(); + val code = adminConfig.deployableCode; assert(code != null, TokenPool_Error.Unauthorized); return AutoDeployAddress { stateInit: ContractState { @@ -1722,8 +1679,7 @@ fun TokenPool.failLockOrBurnTransfer( /// @dev Sends AskToTransfer to the pool's jetton wallet with original sender as recipient. fun TokenPool.returnTransferLocked(self, forwardPayload: TokenPool_LockOrBurnForwardPayload) { val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); + val details = lazy requestMsg.request.load().transfer.details.load(); val returnTransfer = createMessage({ bounce: BounceMode.NoBounce, @@ -1775,8 +1731,8 @@ fun TokenPool.onLockOrBurnTransferFinalize( val requestMsg = lazy fwdPayload.requestMsg.load(); val prepared = lazy fwdPayload.prepared.load(); - val request = requestMsg.request.load(); - val details = request.transfer.details.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); emit(TOPIC_TOKEN_POOL_LOCKED_OR_BURNED, TokenPool_LockedOrBurned { remoteChainSelector: details.remoteChainSelector, details: TokenPool_LockedOrBurnedDetails { @@ -1802,8 +1758,18 @@ fun TokenPool.onLockOrBurnTransferFinalize( } } -/// Validates the release or mint input and calls postflightCheck. +/// Validates the release or mint input for correctness on +/// - token to be released or minted +/// - RMN curse status +/// - if the sender is the configured Router +/// - if the source pool is configured for the remote chain +/// - rate limiting for either default or FTF transfer messages. +/// - postflight checks hooks (if enabled) +/// /// @param msg The full ReleaseOrMint message containing all request fields. +/// +/// @dev This function should always be called before executing a release or mint. Not doing so +/// would allow for various exploits. fun TokenPool.validateReleaseOrMint( mutate self, sender: address, @@ -1817,12 +1783,12 @@ fun TokenPool.validateReleaseOrMint( return (wait, prepared); } - val request = msg.request.load(); - val details = request.transfer.details.load(); + val request = lazy msg.request.load(); + val details = lazy request.transfer.details.load(); assert(self.isSupportedToken(details.localToken), TokenPool_Error.InvalidToken); self.requireSupportedChain(details.remoteChainSelector); self.ensureNotCursed(details.remoteChainSelector); - self.ensureInboundAccess(sender, details.remoteChainSelector); + self.onlyRouter(sender); assert(self.isRemotePool(details.remoteChainSelector, request.sourcePoolAddress), TokenPool_Error.InvalidRemotePoolForChain); val remoteDecimals = self.parseRemoteDecimals(request.sourcePoolData); @@ -1831,7 +1797,8 @@ fun TokenPool.validateReleaseOrMint( // Consume rate limit BEFORE hook check — _consume throws BucketOverfilled // designed to bounce to the original sender, not to the hooks contract. if (msg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { - TokenPool.ensureRequestedFinalityAllowed(msg.requestedFinalityConfig, self.data.adminConfig.load().allowedFinalityConfig); + val adminConfig = lazy self.data.adminConfig.load(); + TokenPool.ensureRequestedFinalityAllowed(msg.requestedFinalityConfig, adminConfig.allowedFinalityConfig); self.consumeFastFinalityInboundRateLimit(details.remoteChainSelector, localAmount); } else { self.consumeInboundRateLimit(details.remoteChainSelector, localAmount); @@ -1900,8 +1867,8 @@ fun TokenPool.onReleaseOrMintFinalize( msg: TokenPool_ReleaseOrMint, prepared: TokenPool_ReleaseOrMintPrepared, ): void { - val request = msg.request.load(); - val details = request.transfer.details.load(); + val request = lazy msg.request.load(); + val details = lazy request.transfer.details.load(); emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { remoteChainSelector: details.remoteChainSelector, details: TokenPool_ReleasedOrMintedDetails { @@ -1985,9 +1952,9 @@ fun TokenPool.requireSupportedChain(self, remoteChainSelector: uint64) { /// Checks whether the msg.sender is either the owner or the rate limit admin. fun TokenPool.onlyOwnerOrRateLimitAdmin(self, sender: address) { - val adminConfig = self.data.adminConfig.load(); - val dynamicConfig = adminConfig.dynamicConfig.load(); - val ownable = adminConfig.ownable.load(); + val adminConfig = lazy self.data.adminConfig.load(); + val dynamicConfig = lazy adminConfig.dynamicConfig.load(); + val ownable = lazy adminConfig.ownable.load(); val senderIsRateLimitAdmin = fun () { return dynamicConfig.rateLimitAdmin != null && sender == dynamicConfig.rateLimitAdmin; @@ -2000,41 +1967,37 @@ fun TokenPool.onlyOwnerOrRateLimitAdmin(self, sender: address) { /// Checks whether the msg.sender is either the owner or the RMN proxy. fun TokenPool.onlyOwnerOrRMNProxy(self, sender: address) { - val adminConfig = self.data.adminConfig.load(); - val ownable = adminConfig.ownable.load(); + val adminConfig = lazy self.data.adminConfig.load(); + val ownable = lazy adminConfig.ownable.load(); assert( - sender == ownable.owner || sender == adminConfig.rmnProxy, + sender == ownable.owner || (adminConfig.rmnProxy != null && sender == adminConfig.rmnProxy!), TokenPool_Error.Unauthorized ); } -fun TokenPool.ensureNotCursed(self, remoteChainSelector: uint64) { - if (self.hooks != null && self.hooks.ensureNotCursed != null) { - return self.hooks.ensureNotCursed(self.context, remoteChainSelector); - } - assert(self.verifyNotCursed(remoteChainSelector as uint128), TokenPool_Error.CursedByRMN); +/// Restricts CCIP token operations to the configured Router. The Router owns ramp authentication +/// and applies its own RMN policy; the pool applies its local cursed-subject policy independently. +fun TokenPool.onlyRouter(self, sender: address) { + val adminConfig = lazy self.data.adminConfig.load(); + val dynamicConfig = lazy adminConfig.dynamicConfig.load(); + assert(sender == dynamicConfig.router, TokenPool_Error.Unauthorized); } -fun TokenPool.ensureOutboundAccess(self, sender: address, remoteChainSelector: uint64) { - if (self.hooks != null && self.hooks.ensureOutboundAccess != null) { - self.hooks.ensureOutboundAccess(self.context, sender, remoteChainSelector); - - return; - } - - val onRamp = self.getOnRamp(remoteChainSelector); - assert(onRamp != null && onRamp! == sender, TokenPool_Error.Unauthorized); +/// Authenticates asynchronous pool-hook continuations. Without this boundary, +/// an arbitrary sender could forge a success/failure continuation after the +/// Router-authenticated operation returned to the async flow. +fun TokenPool.onlyAdvancedPoolHooks(self, sender: address) { + val adminConfig = lazy self.data.adminConfig.load(); + val hooks = adminConfig.advancedPoolHooks; + assert(hooks != null && sender == hooks!, TokenPool_Error.Unauthorized); } -fun TokenPool.ensureInboundAccess(self, sender: address, remoteChainSelector: uint64) { - if (self.hooks != null && self.hooks.ensureInboundAccess != null) { - self.hooks.ensureInboundAccess(self.context, sender, remoteChainSelector); +fun TokenPool.ensureNotCursed(self, remoteChainSelector: uint64) { + assert(self.verifyNotCursed(remoteChainSelector as uint128), TokenPool_Error.CursedByRMN); - return; + if (self.hooks != null && self.hooks.ensureNotCursed != null) { + return self.hooks.ensureNotCursed(self.context, remoteChainSelector); } - - val offRamp = self.getOffRamp(remoteChainSelector); - assert(offRamp != null && offRamp! == sender, TokenPool_Error.Unauthorized); } fun TokenPool.preflightCheck( @@ -2049,10 +2012,11 @@ fun TokenPool.preflightCheck( } // Check if on-chain async hooks are configured. - val hooks = self.data.adminConfig.load().advancedPoolHooks; + val adminConfig = lazy self.data.adminConfig.load(); + val hooks = adminConfig.advancedPoolHooks; if (hooks != null) { // amountPostFee is already computed in fwdPayload.prepared.destTokenAmount. - val prepared = fwdPayload.prepared.load(); + val prepared = lazy fwdPayload.prepared.load(); // Send async preflight check to hooks with replyPayload for callback resumption. val hooksMsg = createMessage({ @@ -2089,10 +2053,11 @@ fun TokenPool.postflightCheck( } // Check if on-chain async hooks are configured. - val hooks = self.data.adminConfig.load().advancedPoolHooks; + val adminConfig = lazy self.data.adminConfig.load(); + val hooks = adminConfig.advancedPoolHooks; if (hooks != null) { // localAmount is already computed in fwdPayload.prepared.localAmount. - val prepared = fwdPayload.prepared.load(); + val prepared = lazy fwdPayload.prepared.load(); // Send async postflight check to hooks with replyPayload for callback resumption. val hooksMsg = createMessage({ diff --git a/contracts/contracts/ccip/pools/lib/token_pool/events.tolk b/contracts/contracts/ccip/pools/lib/token_pool/events.tolk index fa18e78f3..d71d63f32 100644 --- a/contracts/contracts/ccip/pools/lib/token_pool/events.tolk +++ b/contracts/contracts/ccip/pools/lib/token_pool/events.tolk @@ -10,7 +10,6 @@ const TOPIC_TOKEN_POOL_CHAIN_REMOVED = "TokenPool_ChainRemoved".crc32(); const TOPIC_TOKEN_POOL_REMOTE_POOL_ADDED = "TokenPool_RemotePoolAdded".crc32(); const TOPIC_TOKEN_POOL_REMOTE_POOL_REMOVED = "TokenPool_RemotePoolRemoved".crc32(); const TOPIC_TOKEN_POOL_DYNAMIC_CONFIG_SET = "TokenPool_DynamicConfigSet".crc32(); -const TOPIC_TOKEN_POOL_RAMP_ACCESS_UPDATED = "TokenPool_RampAccessUpdated".crc32(); const TOPIC_TOKEN_POOL_FINALITY_CONFIG_SET = "TokenPool_FinalityConfigSet".crc32(); const TOPIC_TOKEN_POOL_RATE_LIMIT_CONFIGURED = "TokenPool_RateLimitConfigured".crc32(); const TOPIC_TOKEN_POOL_FEE_CONFIG_UPDATED = "TokenPool_TokenTransferFeeConfigUpdated".crc32(); @@ -155,17 +154,10 @@ struct TokenPool_RateLimitConfigured { // ); // event AdvancedPoolHooksUpdated(IAdvancedPoolHooks oldHook, IAdvancedPoolHooks newHook); -struct TokenPool_RampAccessUpdated { - remoteChainSelector: uint64; - onRamp: address? = null; - offRamp: address? = null; -} - type TokenPool_Events = | TokenPool_ChainAdded | TokenPool_ChainRemoved | TokenPool_RateLimitConfigured - | TokenPool_RampAccessUpdated | TokenPool_TokenTransferFeeConfigUpdated | TokenPool_TokenTransferFeeConfigDeleted | TokenPool_OutboundRateLimitConsumed diff --git a/contracts/contracts/ccip/pools/lib/token_pool/messages.tolk b/contracts/contracts/ccip/pools/lib/token_pool/messages.tolk index cc7481d18..02754125d 100644 --- a/contracts/contracts/ccip/pools/lib/token_pool/messages.tolk +++ b/contracts/contracts/ccip/pools/lib/token_pool/messages.tolk @@ -2,6 +2,7 @@ import "../../../../lib/jetton/messages" import "../../../../lib/funding/jetton_withdrawable" import "../../../../lib/utils" + import "../../../common/types" import "../../../rmn_remote/lib" @@ -60,7 +61,7 @@ struct (0x89d602e5) TokenPool_DeployableCodeSet { /// `TokenPool_AdminConfig.deployableCode` is registered. Replaces any previous set. struct (0x84384142) TokenPool_SetAllowedDepositNamespaces { queryId: uint64; - allowedDepositNamespaces: map; + allowedDepositNamespaces: map; } /// Replied on `TokenPool_SetAllowedDepositNamespaces` to confirm tx and return excess to sender. @@ -79,15 +80,10 @@ struct (0x30a1d1f7) TokenPool_ApplyTokenTransferFeeConfigUpdates { disableChainSelectors: SnakedCell; } -struct (0xe30764be) TokenPool_UpdateRampAccess { - queryId: uint64; - updates: SnakedCell; -} - /// Sets the RMN proxy address, a role which is responsible for updating the cursed subjects list. struct (0x9929b642) TokenPool_SetRMNProxy { queryId: uint64; - rmnProxy: address; + rmnProxy: address?; } struct (0x9da4da09) TokenPool_SetCursedSubjects { @@ -277,7 +273,6 @@ type TokenPool_InMessage = | TokenPool_SetAllowedDepositNamespaces | TokenPool_SetRateLimitConfig | TokenPool_ApplyTokenTransferFeeConfigUpdates - | TokenPool_UpdateRampAccess | TokenPool_SetRMNProxy | TokenPool_SetCursedSubjects | JettonWithdrawable_Withdraw; @@ -346,7 +341,7 @@ struct (0xdd7b0c71) TokenPool_RateLimitConfiguredNotification { /// Replied on `TokenPool_SetRMNProxy` to confirm tx and return excess to sender. struct (0xe5d08b2e) TokenPool_RMNProxySet { queryId: uint64; - rmnProxy: address; + rmnProxy: address?; } /// Replied on `TokenPool_SetCursedSubjects` to confirm tx and return excess to sender. @@ -360,11 +355,6 @@ struct (0xad7833d7) TokenPool_ChainUpdatesApplied { queryId: uint64; } -/// Replied on `TokenPool_UpdateRampAccess` to confirm tx and return excess to sender. -struct (0xd7f5c563) TokenPool_RampAccessUpdatesApplied { - queryId: uint64; -} - /// Replied on `TokenPool_ApplyTokenTransferFeeConfigUpdates` to confirm tx and return excess to sender. struct (0x28cbcc64) TokenPool_FeeConfigApplied { queryId: uint64; @@ -396,5 +386,4 @@ type TokenPool_OutMessage = | TokenPool_CursedSubjectsSet | TokenPool_AdvancedPoolHooksSet | TokenPool_ChainUpdatesApplied - | TokenPool_RampAccessUpdatesApplied | TokenPool_FeeConfigApplied; diff --git a/contracts/contracts/ccip/pools/lib/token_pool/types.tolk b/contracts/contracts/ccip/pools/lib/token_pool/types.tolk index dbfcfdacd..8837bb467 100644 --- a/contracts/contracts/ccip/pools/lib/token_pool/types.tolk +++ b/contracts/contracts/ccip/pools/lib/token_pool/types.tolk @@ -30,21 +30,16 @@ struct TokenPool_DynamicConfig { /// @dev This field is part of dynamic config so it can be updated without redeploying the /// pool. Only namespaces whose deployable code is registered (see `TokenPool_AdminConfig`) /// are meaningful. - allowedDepositNamespaces: map = []; + allowedDepositNamespaces: map = []; } -struct TokenPool_MirroredPolicy { - onRamps: map = []; - offRamps: map = []; +struct TokenPool_LocalPolicy { + // Independently managed pool-local RMN policy. This is intentionally not a + // mirror of the Router's cursed subjects: the pool owner may configure a + // distinct RMN proxy, or disable proxy updates by setting it to addr_none. cursedSubjects: CursedSubjects; } -struct TokenPool_RampUpdate { - remoteChainSelector: uint64; - onRamp: address? = null; - offRamp: address? = null; -} - struct TokenPool_RateLimiterPair { outbound: Cell; inbound: Cell; diff --git a/contracts/contracts/ccip/pools/lock_release/contract.tolk b/contracts/contracts/ccip/pools/lock_release/contract.tolk index a6fa0bdf3..1124faaea 100644 --- a/contracts/contracts/ccip/pools/lock_release/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release/contract.tolk @@ -34,6 +34,8 @@ contract LockReleaseTokenPool { incomingMessages: | TokenPool_InMessage | LockReleaseTokenPool_InMessage + + forceAbiExport: (LockReleaseTokenPool_ReleaseContext) } const CONTRACT_VERSION = "0.1.0".literalSlice(); @@ -338,8 +340,6 @@ fun loadPool(st: Storage): TokenPool { data: st.poolData.load(), context: st, hooks: TokenPool_Hooks { - ensureOutboundAccess: null, - ensureInboundAccess: null, ensureNotCursed: null, onLockOrBurn: null, validateLockOrBurn: null, @@ -437,22 +437,12 @@ get fun isSupportedChain(remoteChainSelector: uint64): bool { return Storage.load().poolData.load().remoteChainConfigs.get(remoteChainSelector).isFound; } -get fun onRamp(remoteChainSelector: uint64): address? { - val entry = Storage.load().poolData.load().mirroredPolicy.load().onRamps.get(remoteChainSelector); - return entry.isFound ? entry.loadValue() : null; -} - -get fun offRamp(remoteChainSelector: uint64): address? { - val entry = Storage.load().poolData.load().mirroredPolicy.load().offRamps.get(remoteChainSelector); - return entry.isFound ? entry.loadValue() : null; -} - -get fun getRMNProxy(): address { +get fun getRMNProxy(): address? { return Storage.load().poolData.load().adminConfig.load().rmnProxy; } get fun verifyNotCursed(subject: uint128): bool { - return !Storage.load().poolData.load().mirroredPolicy.load().cursedSubjects.isCursed(subject); + return !Storage.load().poolData.load().localPolicy.load().cursedSubjects.isCursed(subject); } get fun owner(): address { diff --git a/contracts/contracts/ccip/pools/lock_release/storage.tolk b/contracts/contracts/ccip/pools/lock_release/storage.tolk index 8516ca837..acc2ad36a 100644 --- a/contracts/contracts/ccip/pools/lock_release/storage.tolk +++ b/contracts/contracts/ccip/pools/lock_release/storage.tolk @@ -44,9 +44,7 @@ fun Storage.init(config: LockReleaseTokenPool_InitConfig): Storage { advancedPoolHooks: createAddressNone() as address, deployableCode: config.deployableCode, }.toCell(), - mirroredPolicy: TokenPool_MirroredPolicy { - onRamps: createEmptyMap(), - offRamps: createEmptyMap(), + localPolicy: TokenPool_LocalPolicy { cursedSubjects: CursedSubjects { data: createEmptyMap(), }, diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk index 0f64c5fea..a0527550f 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk @@ -34,6 +34,11 @@ contract LockReleaseLockboxTokenPool { incomingMessages: | TokenPool_InMessage | LockReleaseLockboxTokenPool_InMessage + + forceAbiExport: ( + LockReleaseLockboxTokenPool_LockContext, + LockReleaseLockboxTokenPool_ReleaseContext + ) } const CONTRACT_VERSION = "0.1.0".literalSlice(); @@ -451,8 +456,6 @@ fun loadPool(st: Storage): TokenPool { data: st.poolData.load(), context: st, hooks: TokenPool_Hooks { - ensureOutboundAccess: null, - ensureInboundAccess: null, ensureNotCursed: null, onLockOrBurn: null, validateLockOrBurn: null, @@ -498,22 +501,12 @@ get fun isSupportedChain(remoteChainSelector: uint64): bool { return Storage.load().poolData.load().remoteChainConfigs.get(remoteChainSelector).isFound; } -get fun onRamp(remoteChainSelector: uint64): address? { - val entry = Storage.load().poolData.load().mirroredPolicy.load().onRamps.get(remoteChainSelector); - return entry.isFound ? entry.loadValue() : null; -} - -get fun offRamp(remoteChainSelector: uint64): address? { - val entry = Storage.load().poolData.load().mirroredPolicy.load().offRamps.get(remoteChainSelector); - return entry.isFound ? entry.loadValue() : null; -} - -get fun getRMNProxy(): address { +get fun getRMNProxy(): address? { return Storage.load().poolData.load().adminConfig.load().rmnProxy; } get fun verifyNotCursed(subject: uint128): bool { - return !Storage.load().poolData.load().mirroredPolicy.load().cursedSubjects.isCursed(subject); + return !Storage.load().poolData.load().localPolicy.load().cursedSubjects.isCursed(subject); } get fun owner(): address { diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk index ad9cc2d3f..c699a6dca 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk @@ -44,9 +44,7 @@ fun Storage.init(config: LockReleaseLockboxTokenPool_InitConfig): Storage { advancedPoolHooks: createAddressNone() as address, deployableCode: config.deployableCode, }.toCell(), - mirroredPolicy: TokenPool_MirroredPolicy { - onRamps: createEmptyMap(), - offRamps: createEmptyMap(), + localPolicy: TokenPool_LocalPolicy { cursedSubjects: CursedSubjects { data: createEmptyMap(), }, diff --git a/contracts/contracts/ccip/router/contract.tolk b/contracts/contracts/ccip/router/contract.tolk index 0308791c1..391065df7 100644 --- a/contracts/contracts/ccip/router/contract.tolk +++ b/contracts/contracts/ccip/router/contract.tolk @@ -11,21 +11,20 @@ import "../../lib/receiver/messages" import "../../lib/jetton/jetton_client" import "../rmn_remote/lib.tolk" +import "../common/types" import "../onramp/messages" import "../onramp/types" import "../offramp/messages" import "../offramp/types" import "../rmn_remote/lib" import "../pools/lib/token_pool/messages" +import "../pools/lib/token_pool/types" import "types" import "messages" import "storage" import "errors" import "events" -import "../pools/lib/token_pool/types" -import "../common/types" -import "../../lib/jetton/jetton_client" tolk 1.4.2 @@ -90,6 +89,7 @@ fun onInternalMessage(in: InMessage) { val message = lazy msg.message.load(); val expectedOffRamp = st.offRamps.mustGet(message.sourceChainSelector, Router_Error.SourceChainNotEnabled); assert(in.senderAddress == expectedOffRamp, Router_Error.SenderIsNotOffRamp); + assertNotCursed(st, message.sourceChainSelector); onRouteMessage(msg, msg.message); } // Sender must be OnRamp @@ -175,8 +175,37 @@ fun onInternalMessage(in: InMessage) { val request = lazy msg.request.load(); val details = lazy request.transfer.details.load(); assertSenderIsOnRamp(st, details.remoteChainSelector, in.senderAddress); + if (isCursed(st, details.remoteChainSelector)) { + return sendLockOrBurnRelayFailure( + msg.executorAddress, + msg.queryID, + msg.tokenPool, + Router_Error.SubjectCursed as int32, + ); + } onLockOrBurn(msg); } + // Sender must be the OffRamp registered for the request's source chain. + Router_RelayReleaseOrMint => { + val st = lazy Storage.load(); + val expectedOffRamp = st.offRamps.mustGet(msg.sourceChainSelector, Router_Error.SourceChainNotEnabled); + assert(in.senderAddress == expectedOffRamp, Router_Error.SenderIsNotOffRamp); + + val request = lazy msg.request.load(); + val details = lazy request.transfer.details.load(); + assert(details.remoteChainSelector == msg.sourceChainSelector, Router_Error.SourceChainSelectorMismatch); + if (isCursed(st, msg.sourceChainSelector)) { + return sendReleaseOrMintRelayFailure( + expectedOffRamp, + msg.sourceChainSelector, + msg.replyTo, + msg.queryID, + msg.tokenPool, + Router_Error.SubjectCursed as int32, + ); + } + onRelayReleaseOrMint(msg); + } // Sender must be a Jetton Wallet owned by the Router TransferNotificationForRecipient => { // TODO we can't just throw errors in the Router on JettonTransferNotification - this will burn the tokens. @@ -224,6 +253,58 @@ fun onInternalMessage(in: InMessage) { } fun onBouncedMessage(in: InMessageBounced) { + if (in.bouncedBody.preloadUint(32) == 0xfffffffe) { + val rich = lazy RichBounceBody.fromSlice(in.bouncedBody); + val richMsg = lazy Router_RichBouncedMessage.fromCell(rich.originalBody); + match (richMsg) { + TokenPool_LockOrBurn => { + if (richMsg.replyTo != null) { + sendLockOrBurnRelayFailure( + richMsg.replyTo!, + richMsg.queryId, + in.senderAddress, + rich.exitCode, + ); + } + } + TokenPool_ReleaseOrMint => { + if (richMsg.replyTo != null) { + // Recover the lane from the request and route the failure + // back through its OffRamp. + val request = lazy richMsg.request.load(); + val details = lazy request.transfer.details.load(); + val st = lazy Storage.load(); + val offRamp = st.offRamps.mustGet(details.remoteChainSelector, Router_Error.SourceChainNotEnabled); + sendReleaseOrMintRelayFailure( + offRamp, + details.remoteChainSelector, + richMsg.replyTo!, + richMsg.queryId, + in.senderAddress, + rich.exitCode, + ); + } + } + AskToTransfer => { + // Bounced `Router_WithdrawToTokenPool` withdrawal: the jettons never reached the + // token pool, so the send is stuck and the executor waits for a reply that will + // never come. + // + // TODO: propagate the failure to the OnRamp so it can fail the send and the + // executor can settle. Requires a failure message plus an executor `replyTo` for + // this leg, which the Router does not carry. The withdrawal is being reworked to + // route through an on-ramp deposit account, which will own this failure path. + emit(TOKEN_POOL_WITHDRAW_BOUNCED_TOPIC, TokenPoolWithdrawBounced { + wallet: in.senderAddress, + tokenPool: richMsg.transferRecipient, + queryId: richMsg.queryId, + exitCode: rich.exitCode, + }); + } + } + return; + } + val msg = lazy Router_BouncedMessage.fromSlice(in.bouncedBody.skipBouncedPrefix()); match (msg) { // We are lazy matching and only accessing the first element of the struct. @@ -707,7 +788,7 @@ withdraw path should go through the executor - so the TP needs to withdraw -> ex */ fun onLockOrBurn(msg: Router_LockOrBurn) { val tokenPoolLockOrBurn = createMessage({ - bounce: true, + bounce: BounceMode.RichBounce, value: 0, dest: msg.tokenPool, body: TokenPool_LockOrBurn { @@ -719,7 +800,22 @@ fun onLockOrBurn(msg: Router_LockOrBurn) { } }); tokenPoolLockOrBurn.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); -} +} + +fun onRelayReleaseOrMint(msg: Router_RelayReleaseOrMint) { + val tokenPoolReleaseOrMint = createMessage({ + bounce: BounceMode.RichBounce, + value: 0, + dest: msg.tokenPool, + body: TokenPool_ReleaseOrMint { + queryId: msg.queryID, + request: msg.request, + requestedFinalityConfig: msg.requestedFinalityConfig, + replyTo: msg.replyTo, + }, + }); + tokenPoolReleaseOrMint.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); +} /* Handles contract upgrade requests using the Upgradeable pattern. Invokes the @@ -737,6 +833,59 @@ fun assertSenderIsOnRamp(st: Storage, destChainSelector: uint64, sender: address assert (sender == onRampAddress) throw Router_Error.NotOnRamp; } +/// Enforces the Router's authoritative RMN policy for a remote chain. +/// +/// Token pools can additionally enforce an independently managed local curse +/// policy. A transfer is admitted only when both policy domains allow it. +fun assertNotCursed(st: Storage, remoteChainSelector: uint64) { + assert(!isCursed(st, remoteChainSelector), Router_Error.SubjectCursed); +} + +fun isCursed(st: Storage, remoteChainSelector: uint64): bool { + return st.rmnRemote.load().cursedSubjects.isCursed(remoteChainSelector as uint128); +} + +fun sendLockOrBurnRelayFailure( + executor: address, + queryID: uint64, + tokenPool: address, + exitCode: int32, +) { + val failed = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: executor, + body: Router_TokenPoolLockOrBurnFailed { queryID, tokenPool, exitCode }, + }); + failed.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); +} + +/// Reports an inbound release/mint failure to the OffRamp that owns the lane. +/// The OffRamp is the executor's sole authority (mirrors EVM), so the Router +/// never talks to the executor directly. `replyTo` is the executor to fail. +fun sendReleaseOrMintRelayFailure( + offRamp: address, + sourceChainSelector: uint64, + replyTo: address, + queryID: uint64, + tokenPool: address, + exitCode: int32, +) { + val failed = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: offRamp, + body: Router_TokenPoolReleaseOrMintFailed { + queryID, + sourceChainSelector, + replyTo, + tokenPool, + exitCode, + }, + }); + failed.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); +} + @method_id(1000) fun migrate(storage: cell, version: slice): cell { requireSupportedVersion(version); diff --git a/contracts/contracts/ccip/router/errors.tolk b/contracts/contracts/ccip/router/errors.tolk index 1cfdee07f..bad6b3d2c 100644 --- a/contracts/contracts/ccip/router/errors.tolk +++ b/contracts/contracts/ccip/router/errors.tolk @@ -16,4 +16,5 @@ enum Router_Error { JettonTransferNotificationWithoutForwardPayload TokenAmountMismatch ForwardPayloadIsNotCCIPSend + SourceChainSelectorMismatch } diff --git a/contracts/contracts/ccip/router/events.tolk b/contracts/contracts/ccip/router/events.tolk index 8ec2f5b02..10b6e1f54 100644 --- a/contracts/contracts/ccip/router/events.tolk +++ b/contracts/contracts/ccip/router/events.tolk @@ -34,3 +34,14 @@ struct MessageToOffRampBounced { offRamp: address, execId: uint192, } + +/// Emitted when a `Router_WithdrawToTokenPool` jetton transfer bounces back. The send cannot +/// proceed and the executor is waiting, so this is emitted for observability until the failure +/// is propagated to the OnRamp. +const TOKEN_POOL_WITHDRAW_BOUNCED_TOPIC = "TokenPoolWithdrawBounced".crc32(); +struct TokenPoolWithdrawBounced { + wallet: address, + tokenPool: address, + queryId: uint64, + exitCode: int32, +} diff --git a/contracts/contracts/ccip/router/messages.tolk b/contracts/contracts/ccip/router/messages.tolk index bcaa5110a..47ffc309e 100644 --- a/contracts/contracts/ccip/router/messages.tolk +++ b/contracts/contracts/ccip/router/messages.tolk @@ -30,6 +30,7 @@ type Router_InMsg = | Router_MessageSent | Router_MessageRejected | Router_LockOrBurn + | Router_RelayReleaseOrMint | Router_RMNOwnableMessage | TransferNotificationForRecipient; @@ -42,6 +43,11 @@ type Router_BouncedMessage = | OffRamp_CCIPReceiveConfirm | OffRamp_CCIPReceiveBounced; +type Router_RichBouncedMessage = + | TokenPool_LockOrBurn + | TokenPool_ReleaseOrMint + | AskToTransfer; + // update multiple chain selectors in batch to the same onramp/offramp address struct (0x7db6745d) Router_ApplyRampUpdates { queryId: uint64; @@ -150,6 +156,18 @@ struct (0x6f2d00df) Router_LockOrBurn { executorAddress: address, } +/// Relays an inbound token operation from the registered OffRamp to the pool. +/// The Router binds the authenticated OffRamp selector to the selector encoded +/// in the pool request and applies its own RMN policy before dispatch. +struct (0xb19862a3) Router_RelayReleaseOrMint { + queryID: uint64; + sourceChainSelector: uint64; + tokenPool: address; + request: Cell; + requestedFinalityConfig: uint32; + replyTo: address; +} + struct Router_GetValidatedFeeContext { routerContext: address; // sender userContext: RemainingBitsOrRef, @@ -177,6 +195,28 @@ struct (0xec23c562) Router_MessageValidationFailed { context: RemainingBitsOrRef } +/// Canonical Router failure returned when a LockOrBurn request bounced before +/// the token pool admitted it. +struct (0xb76e3a84) Router_TokenPoolLockOrBurnFailed { + queryID: uint64; + tokenPool: address; + exitCode: int32; +} + +/// Canonical Router failure for an inbound release/mint that the Router could +/// not complete (lane cursed, or the pool bounced the relay). +/// +/// It is sent to the OffRamp that owns the source lane, which is the only +/// authority allowed to fail a ReceiveExecutor. The OffRamp authenticates the +/// Router by `sourceChainSelector` and forwards the failure to `replyTo`. +struct (0xb980cb4e) Router_TokenPoolReleaseOrMintFailed { + queryID: uint64; + sourceChainSelector: uint64; + replyTo: address; + tokenPool: address; + exitCode: int32; +} + // Costs associated with Router operations struct Router_Costs {} diff --git a/contracts/tests/ccip/accounts/DepositAccount.spec.ts b/contracts/tests/ccip/accounts/DepositAccount.spec.ts index e7e3193d2..d2ccea9ad 100644 --- a/contracts/tests/ccip/accounts/DepositAccount.spec.ts +++ b/contracts/tests/ccip/accounts/DepositAccount.spec.ts @@ -9,6 +9,7 @@ import { DepositAccount_InMessageForward, DepositAccount_Reply, } from '../../../wrappers/gen/ccip/DepositAccount' +import { TransferNotificationForRecipient } from '../../../wrappers/gen/ccip/pools/TokenPool' describe('DepositAccount (default forward hook, off-ramp role)', () => { let blockchain: Blockchain @@ -45,15 +46,17 @@ describe('DepositAccount (default forward hook, off-ramp role)', () => { forwardPayload: ForwardPayloadRemainder.fromSlice(Cell.EMPTY.beginParse()), }) - // Build a boxed Jetton `TransferNotificationForRecipient`-shaped body carried by a real wallet. + // Build a boxed Jetton `TransferNotificationForRecipient` body as a real wallet would send it + // (the forward payload is boxed as a maybe_ref; here empty — no CCIPSend carried). const buildNotificationBody = (queryId: bigint, amount: bigint, to: Address) => - beginCell() - .storeUint(0x7362d09c, 32) // TransferNotificationForRecipient - .storeUint(queryId, 64) - .storeCoins(amount) - .storeAddress(to) - .storeMaybeRef(null) - .endCell() + TransferNotificationForRecipient.toCell( + TransferNotificationForRecipient.create({ + queryId, + jettonAmount: amount, + transferInitiator: to, + forwardPayload: beginCell().storeMaybeRef(null).asSlice(), + }), + ) beforeEach(async () => { blockchain = await Blockchain.create() diff --git a/contracts/tests/ccip/accounts/OnRampAccount.spec.ts b/contracts/tests/ccip/accounts/OnRampAccount.spec.ts index e281cbb69..c580ffaab 100644 --- a/contracts/tests/ccip/accounts/OnRampAccount.spec.ts +++ b/contracts/tests/ccip/accounts/OnRampAccount.spec.ts @@ -7,6 +7,7 @@ import { ForwardPayloadRemainder, DepositAccount_WithdrawFailed, } from '../../../wrappers/gen/ccip/OnRampAccount' +import { TransferNotificationForRecipient } from '../../../wrappers/gen/ccip/pools/TokenPool' import { JettonMinter, JettonWallet } from '../../../wrappers/examples/jetton' import { contractCode } from '../../../wrappers/codeLoader' @@ -188,13 +189,15 @@ describe('OnRampAccount (generic DepositAccount with CCIPSend hook)', () => { to: account.address, value: toNano('0.5'), bounce: false, - body: beginCell() - .storeUint(0x7362d09c, 32) // TransferNotificationForRecipient - .storeUint(5n, 64) - .storeCoins(toNano('1')) - .storeAddress(user.address) - .storeMaybeRef(ccipSendCell()) - .endCell(), + body: TransferNotificationForRecipient.toCell( + TransferNotificationForRecipient.create({ + queryId: 5n, + jettonAmount: toNano('1'), + transferInitiator: user.address, + // A real jetton wallet boxes the transfer's forward payload as a maybe_ref. + forwardPayload: beginCell().storeMaybeRef(ccipSendCell()).asSlice(), + }), + ), }) // The CCIPSend (op 0x31768d95) is relayed to the Router. expect(res.transactions).toHaveTransaction({ diff --git a/contracts/tests/ccip/e2e/CCIPSendWithTokenTransfer.spec.ts b/contracts/tests/ccip/e2e/CCIPSendWithTokenTransfer.spec.ts index caf8166d9..17f7b2661 100644 --- a/contracts/tests/ccip/e2e/CCIPSendWithTokenTransfer.spec.ts +++ b/contracts/tests/ccip/e2e/CCIPSendWithTokenTransfer.spec.ts @@ -158,7 +158,7 @@ describe('CCIPSend with token transfer (e2e)', () => { router: router.address, rateLimitAdmin: deployer.address, feeAdmin: deployer.address, - allowedDepositNamespaces: new Map(), + allowedDepositNamespaces: new Set(), }), jettonClient: tp.JettonClient.create({ masterAddress: minter.address, @@ -166,9 +166,7 @@ describe('CCIPSend with token transfer (e2e)', () => { }), advancedPoolHooks: null, }), - mirroredPolicy: tp.TokenPool_MirroredPolicy.create({ - onRamps: new Map(), - offRamps: new Map(), + localPolicy: tp.TokenPool_LocalPolicy.create({ cursedSubjects: tp.CursedSubjects.create({ data: new Set(), }), @@ -225,29 +223,6 @@ describe('CCIPSend with token transfer (e2e)', () => { success: true, }) - // Register the Router as the authorized caller for lock/burn on this chain. - // The Router forwards Router_LockOrBurn on behalf of the OnRamp, so it's the - // sender the pool sees for TokenPool_LockOrBurn. - const rampAccessResult = await tokenPool.sendTokenPoolUpdateRampAccess( - deployer.getSender(), - toNano('0.05'), - { - updates: [ - tp.TokenPool_RampUpdate.create({ - remoteChainSelector: DestChainSelector, - onRamp: router.address, - offRamp: null, - }), - ], - }, - ) - - expect(rampAccessResult.transactions).toHaveTransaction({ - from: deployer.address, - to: tokenPool.address, - success: true, - }) - const registrationResult = await tokenAdminRegistry.sendTokenAdminRegistryRegisterToken( deployer.getSender(), toNano('0.2'), diff --git a/contracts/tests/ccip/offramp/OffRamp.Setup.ts b/contracts/tests/ccip/offramp/OffRamp.Setup.ts index c4bb91d14..5f750103d 100644 --- a/contracts/tests/ccip/offramp/OffRamp.Setup.ts +++ b/contracts/tests/ccip/offramp/OffRamp.Setup.ts @@ -23,7 +23,6 @@ import { contractCode } from '../../../wrappers/codeLoader' import * as ocr from '../../../wrappers/libraries/ocr/MultiOCR3Base' import * as OCR3Logs from '../../../wrappers/libraries/ocr/Logs' import * as deployable from '../../../wrappers/libraries/Deployable' -import { PERMISSIONLESS_EXECUTION_THRESHOLD_SECONDS } from './OffRamp.execute.spec' import { ChainSelectors } from '../../utils/Selectors' import { setupTestFeeQuoter } from '../helpers/SetUp' @@ -38,6 +37,8 @@ import * as da from '../../../wrappers/gen/ccip/DepositAccount' import * as cct from '../../../wrappers/gen/ccip/cct/JettonMinter' import { JettonWallet } from '../../../wrappers/gen/ccip/cct/JettonWallet' +export const PERMISSIONLESS_EXECUTION_THRESHOLD_SECONDS = BigInt(60) + import * as CrossChainAddressCodec from '../../../wrappers/ccip/common/CrossChainAddressCodec' import * as CCIPLogs from '../../../wrappers/ccip/Logs' @@ -938,7 +939,7 @@ export class OffRampWithTokenPoolTestSetup extends OffRampTestSetup { router: this.router.address, rateLimitAdmin: this.deployer.address, feeAdmin: this.deployer.address, - allowedDepositNamespaces: new Map(), + allowedDepositNamespaces: new Set(), }), jettonClient: tp.JettonClient.create({ masterAddress: token, @@ -946,12 +947,8 @@ export class OffRampWithTokenPoolTestSetup extends OffRampTestSetup { }), allowedFinalityConfig: 0n, }), - mirroredPolicy: tp.TokenPool_MirroredPolicy.create({ - onRamps: new Map(), - offRamps: new Map(), - cursedSubjects: tp.CursedSubjects.create({ - data: new Set(), - }), + localPolicy: tp.TokenPool_LocalPolicy.create({ + cursedSubjects: tp.CursedSubjects.create({ data: new Set() }), }), tokenDecimals, remoteChainConfigs: new Map(), @@ -1005,27 +1002,6 @@ export class OffRampWithTokenPoolTestSetup extends OffRampTestSetup { success: true, }) - // Register the OffRamp as the authorized inbound caller (offRamp) for the chain. - const rampAccessResult = await tokenPool.sendTokenPoolUpdateRampAccess( - this.deployer.getSender(), - toNano('0.05'), - { - updates: [ - tp.TokenPool_RampUpdate.create({ - remoteChainSelector, - onRamp: null, - offRamp: this.offRamp.address, - }), - ], - }, - ) - - expect(rampAccessResult.transactions).toHaveTransaction({ - from: this.deployer.address, - to: tokenPool.address, - success: true, - }) - return tokenPool } diff --git a/contracts/tests/ccip/offramp/OffRamp.execute.spec.ts b/contracts/tests/ccip/offramp/OffRamp.execute.spec.ts index 3d63148d7..076cc22d2 100644 --- a/contracts/tests/ccip/offramp/OffRamp.execute.spec.ts +++ b/contracts/tests/ccip/offramp/OffRamp.execute.spec.ts @@ -17,6 +17,7 @@ import * as mr from '../../../wrappers/gen/ccip/MerkleRoot' import * as rx from '../../../wrappers/gen/ccip/ReceiveExecutor' import * as tr from '../../../wrappers/gen/ccip/TestReceiver' import * as of from '../../../wrappers/gen/ccip/OffRamp' +import * as rt from '../../../wrappers/gen/ccip/Router' import * as tp from '../../../wrappers/gen/ccip/pools/TokenPool' import * as trg from '../../../wrappers/gen/ccip/TokenAdminRegistryEntry' @@ -24,11 +25,13 @@ import * as CCIPLogs from '../../../wrappers/ccip/Logs' import { RMNREMOTE_GLOBAL_CURSE_SUBJECT } from '../../../wrappers/ccip/Router' import * as s from './OffRamp.Setup' -import { OffRampWithTokenPoolTestSetup } from './OffRamp.Setup' +import { + OffRampWithTokenPoolTestSetup, + PERMISSIONLESS_EXECUTION_THRESHOLD_SECONDS, +} from './OffRamp.Setup' import { EXECUTE_COST, MIN_TT_GASLIMIT } from '../../../wrappers/ccip/OffRamp' import { codec } from '../../../wrappers/ccip/common/CrossChainAddressCodec' -export const PERMISSIONLESS_EXECUTION_THRESHOLD_SECONDS = BigInt(60) describe('OffRamp - Execute', () => { let blockchain: Blockchain let setup: s.OffRampTestSetup @@ -1851,7 +1854,7 @@ describe('OffRamp - Execute', () => { success: true, }) - // 3. ReceiveExecutor -> OffRamp (ReleaseOrMint) -> TokenPool + // 3. ReceiveExecutor -> OffRamp (ReleaseOrMint) -> Router (relay) -> TokenPool expect(result.transactions).toHaveTransaction({ from: executorAddress, to: setup.offRamp.address, @@ -1860,6 +1863,12 @@ describe('OffRamp - Execute', () => { }) expect(result.transactions).toHaveTransaction({ from: setup.offRamp.address, + to: setup.router.address, + op: rt.Router_RelayReleaseOrMint.PREFIX, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: setup.router.address, to: setup.tokenPool.address, op: tp.TokenPool_ReleaseOrMint.PREFIX, success: true, @@ -1912,6 +1921,29 @@ describe('OffRamp - Execute', () => { // 2. verify the receiver can withdraw the tokens from the escrow account. }) + it('rejects a Router_TokenPoolReleaseOrMintFailed from a non-router sender', async () => { + await setup.setupSourceChainConfig() + + const result = await setup.offRamp.sendRouterTokenPoolReleaseOrMintFailed( + setup.deployer.getSender(), + toNano('0.5'), + { + queryID: 1n, + sourceChainSelector: setup.SOURCE_CHAIN_SELECTOR, + replyTo: setup.deployer.address, + tokenPool: setup.tokenPool.address, + exitCode: 0n, + }, + ) + + expect(result.transactions).toHaveTransaction({ + from: setup.deployer.address, + to: setup.offRamp.address, + success: false, + exitCode: of.OffRamp.Errors['OffRamp_Error.Unauthorized'], + }) + }) + it('forwards a non-empty offchainTokenData cell to the token pool', async () => { const message = setup.createTestMessageWithToken() // Non-empty per-token offchain data blob. The OffRamp must forward it @@ -1938,10 +1970,10 @@ describe('OffRamp - Execute', () => { ) expect(await setup.getTokenBalance()).toEqual(setup.DEFAULT_TOKEN_AMOUNT) - // OffRamp -> TokenPool: the offchain data must survive the whole - // OffRamp -> ReceiveExecutor -> OffRamp -> TokenPool hop. + // Router -> TokenPool: the offchain data must survive the whole + // OffRamp -> ReceiveExecutor -> OffRamp -> Router -> TokenPool hop. const releaseOrMintTx = findTransaction(result.transactions, { - from: setup.offRamp.address, + from: setup.router.address, to: setup.tokenPool.address, op: tp.TokenPool_ReleaseOrMint.PREFIX, success: true, @@ -1964,8 +1996,10 @@ describe('OffRamp - Execute', () => { const result = await setup.executeReport(report) // Token transfer should still complete and notify success. + // The Router is the pool's only inbound entrypoint, so the pool hop + // originates from the Router relay. expect(result.transactions).toHaveTransaction({ - from: setup.offRamp.address, + from: setup.router.address, to: setup.tokenPool.address, op: tp.TokenPool_ReleaseOrMint.PREFIX, success: true, @@ -2069,12 +2103,20 @@ describe('OffRamp - Execute', () => { const result = await setup.executeReport(report) // The token pool should reject the releaseOrMint (rate limit exceeded). + // The Router holds the pool bounce and reports it to the OffRamp, which + // is the executor's only authority and forwards the failure. expect(result.transactions).toHaveTransaction({ - from: setup.offRamp.address, + from: setup.router.address, to: setup.tokenPool.address, op: tp.TokenPool_ReleaseOrMint.PREFIX, success: false, }) + expect(result.transactions).toHaveTransaction({ + from: setup.router.address, + to: setup.offRamp.address, + op: rt.Router_TokenPoolReleaseOrMintFailed.PREFIX, + success: true, + }) expect(result.transactions).toHaveTransaction({ from: setup.offRamp.address, to: setup.receiveExecutorAddress(message), @@ -2108,6 +2150,7 @@ describe('OffRamp - Execute', () => { // First execution fails due to rate limit. const firstResult = await setup.executeReport(report) expect(firstResult.transactions).toHaveTransaction({ + from: setup.router.address, to: setup.tokenPool.address, op: tp.TokenPool_ReleaseOrMint.PREFIX, success: false, @@ -2188,7 +2231,7 @@ describe('OffRamp - Execute', () => { success: true, }) - // 3. ReceiveExecutor -> OffRamp (ReleaseOrMint) -> TokenPool + // 3. ReceiveExecutor -> OffRamp (ReleaseOrMint) -> Router (relay) -> TokenPool expect(result.transactions).toHaveTransaction({ from: executorAddress, to: setup.offRamp.address, @@ -2197,6 +2240,12 @@ describe('OffRamp - Execute', () => { }) expect(result.transactions).toHaveTransaction({ from: setup.offRamp.address, + to: setup.router.address, + op: rt.Router_RelayReleaseOrMint.PREFIX, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: setup.router.address, to: setup.tokenPool.address, op: tp.TokenPool_ReleaseOrMint.PREFIX, success: true, @@ -2307,12 +2356,20 @@ describe('OffRamp - Execute', () => { const result = await setup.executeReport(report) // The token pool should reject the releaseOrMint (rate limit exceeded). + // The Router holds the pool bounce and reports it to the OffRamp, which + // is the executor's only authority and forwards the failure. expect(result.transactions).toHaveTransaction({ - from: setup.offRamp.address, + from: setup.router.address, to: setup.tokenPool.address, op: tp.TokenPool_ReleaseOrMint.PREFIX, success: false, }) + expect(result.transactions).toHaveTransaction({ + from: setup.router.address, + to: setup.offRamp.address, + op: rt.Router_TokenPoolReleaseOrMintFailed.PREFIX, + success: true, + }) expect(result.transactions).toHaveTransaction({ from: setup.offRamp.address, to: setup.receiveExecutorAddress(message), @@ -2347,6 +2404,7 @@ describe('OffRamp - Execute', () => { // First execution fails due to rate limit. const firstResult = await setup.executeReport(report) expect(firstResult.transactions).toHaveTransaction({ + from: setup.router.address, to: setup.tokenPool.address, op: tp.TokenPool_ReleaseOrMint.PREFIX, success: false, diff --git a/contracts/tests/ccip/offramp/OffRamp.spec.ts b/contracts/tests/ccip/offramp/OffRamp.spec.ts index 9df2c70f6..b7977ab1d 100644 --- a/contracts/tests/ccip/offramp/OffRamp.spec.ts +++ b/contracts/tests/ccip/offramp/OffRamp.spec.ts @@ -22,7 +22,7 @@ import { generateMockTonAddress } from '../../../src/utils' import { errorCode, facilityId } from '../../../wrappers/utils' import { crc32 } from 'zlib' import { ChainSelectors } from '../../utils/Selectors' -import { PERMISSIONLESS_EXECUTION_THRESHOLD_SECONDS } from './OffRamp.execute.spec' +import { PERMISSIONLESS_EXECUTION_THRESHOLD_SECONDS } from './OffRamp.Setup' describe('OffRamp - TypeAndVersion Tests', () => { const currentVersionSpec = TypeAndVersionSpec.newInstance({ diff --git a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts index 1694da739..d91146ee9 100644 --- a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts @@ -11,7 +11,6 @@ import { TokenPool, TokenPool_Data, TokenPool_AdminConfig, - TokenPool_RampUpdate, TokenPool_RateLimitConfigPair, TokenPool_ChainUpdate, TokenPool_LockOrBurn, @@ -21,26 +20,30 @@ import { TokenPool_LockOrBurnForwardPayload, TokenPool_LockOrBurnPrepared, TokenPool_LockOrBurnOutV1, + TokenPool_LockOrBurnWithdraw, TokenPool_ReleaseOrMintInV1, TokenPool_ReleaseOrMintFinished, - TokenPool_MirroredPolicy, + TokenPool_LocalPolicy, TokenPool_DynamicConfig, TokenPool_Transfer, TokenPool_TransferDetails, TokenPool_TokenTransferFeeConfig, TokenPool_TokenTransferFeeConfigArgs, } from '../../../wrappers/gen/ccip/pools/TokenPool' -import { TokenPool_LockOrBurnWithdraw } from '../../../wrappers/gen/ccip/pools/BurnMintTokenPool' -import { BurnMintTokenPool, JettonClient } from '../../../wrappers/gen/ccip/pools/BurnMintTokenPool' +import { + JettonClient, + BurnMintTokenPool, + BurnMintTokenPool_BurnContext, +} from '../../../wrappers/gen/ccip/pools/BurnMintTokenPool' import { CCT_ReturnExcessesBack } from '../../../wrappers/gen/ccip/cct/JettonMinter' -import { runTokenPoolBehaviorTests } from './TokenPool.behavior' -import { runTokenPoolAsyncHookBehaviorTests } from './TokenPool.asyncHook.behavior' -import { runTokenPoolWithdrawFeeTokensBehaviorTests } from './TokenPool.withdrawFeeTokens.behavior' -import { runTokenPoolCcvFeesBehaviorTests } from './TokenPool.ccvFees.behavior' import { MockAdvancedPoolHooks } from '../../../wrappers/gen/ccip/test/MockAdvancedPoolHooks' import * as CrossChainAddressCodec from '../../../wrappers/ccip/common/CrossChainAddressCodec' import { contractCode } from '../../../wrappers/codeLoader' import { DepositAccount } from '../../../wrappers/gen/ccip/DepositAccount' +import { runTokenPoolBehaviorTests } from './TokenPool.behavior' +import { runTokenPoolAsyncHookBehaviorTests } from './TokenPool.asyncHook.behavior' +import { runTokenPoolWithdrawFeeTokensBehaviorTests } from './TokenPool.withdrawFeeTokens.behavior' +import { runTokenPoolCcvFeesBehaviorTests } from './TokenPool.ccvFees.behavior' // Builds a forged `CCT_ReturnExcessesBack` carrying a burn continuation payload. It is sent // from an unauthorized sender, so the pool must reject it (sender not the CCT minter). @@ -49,7 +52,7 @@ function buildForgedCCTReturnExcessesBack(poolAddress: Address): Cell { CCT_ReturnExcessesBack.create({ queryId: 999n, initiator: poolAddress, - forwardPayload: beginCell().storeUint(0xba302a47, 32).endCell(), + forwardPayload: beginCell().storeUint(BurnMintTokenPool_BurnContext.PREFIX, 32).endCell(), }), ) } @@ -118,7 +121,7 @@ describe('BurnMintTokenPool', () => { router: deployer.address, rateLimitAdmin: null, feeAdmin: null, - allowedDepositNamespaces: new Map(), + allowedDepositNamespaces: new Set(), }), jettonClient: JettonClient.create({ masterAddress: cctMinter.address, @@ -127,9 +130,7 @@ describe('BurnMintTokenPool', () => { allowedFinalityConfig: 0n, advancedPoolHooks: null, }), - mirroredPolicy: TokenPool_MirroredPolicy.create({ - onRamps: new Map(), - offRamps: new Map(), + localPolicy: TokenPool_LocalPolicy.create({ cursedSubjects: CursedSubjects.create({ data: new Set(), }), @@ -184,17 +185,6 @@ describe('BurnMintTokenPool', () => { }) } - await burnMintPool.sendTokenPoolUpdateRampAccess(deployer.getSender(), toNano('0.2'), { - queryId: 2n, - updates: [ - TokenPool_RampUpdate.create({ - remoteChainSelector, - onRamp: deployer.address, - offRamp: offRamp.address, - }), - ], - }) - // Mint user-side test balance before handing minter admin to the pool. const mintToOnRamp = await cctMinterRuntime.sendMint(deployer.getSender(), { value: toNano('1'), @@ -267,6 +257,7 @@ describe('BurnMintTokenPool', () => { runTokenPoolBehaviorTests('BurnMintTokenPool', async () => ({ pool, + blockchain, deployer, offRamp, altOffRamp: deployer, @@ -310,6 +301,7 @@ describe('BurnMintTokenPool', () => { return { pool, + blockchain, deployer, offRamp, unauthorized, @@ -757,7 +749,7 @@ describe('BurnMintTokenPool', () => { it('mints tokens on releaseOrMint path and finalizes through the executor notification', async () => { const result = await burnMintPool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.6'), { queryId: 22n, @@ -782,7 +774,7 @@ describe('BurnMintTokenPool', () => { ) expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: burnMintPool.address, success: true, }) @@ -808,7 +800,7 @@ describe('BurnMintTokenPool', () => { it('mints on releaseOrMint with null replyTo without emitting response message', async () => { const result = await burnMintPool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.6'), { queryId: 305n, @@ -833,7 +825,7 @@ describe('BurnMintTokenPool', () => { ) expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: burnMintPool.address, success: true, }) diff --git a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts index 59727a099..3d28b85ca 100644 --- a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts @@ -10,7 +10,7 @@ import { TokenPool_Data, TokenPool_AdminConfig, TokenPool_DynamicConfig, - TokenPool_MirroredPolicy, + TokenPool_LocalPolicy, TokenPool_ReleaseOrMintFinished, TokenPool_LockOrBurn, TokenPool_LockOrBurnForwardPayload, @@ -19,7 +19,6 @@ import { TokenPool_LockOrBurnInV1, TokenPool_ReleaseOrMintInV1, TokenPool_RateLimitConfigPair, - TokenPool_RampUpdate, TokenPool_ChainUpdate, Ownable2Step, TokenPool_TransferDetails, @@ -33,22 +32,22 @@ import { } from '../../../wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool' import { JettonLockBox, + AccessControl_Data, JettonLockBox_WithdrawExtra, } from '../../../wrappers/gen/ccip/pools/JettonLockBox' import { ContractClient as AccessControlClient } from '../../../wrappers/lib/access/AccessControl' -import { runTokenPoolBehaviorTests } from './TokenPool.behavior' -import { runTokenPoolAsyncHookBehaviorTests } from './TokenPool.asyncHook.behavior' -import { runTokenPoolWithdrawFeeTokensBehaviorTests } from './TokenPool.withdrawFeeTokens.behavior' -import { runTokenPoolCcvFeesBehaviorTests } from './TokenPool.ccvFees.behavior' import { MockAdvancedPoolHooks } from '../../../wrappers/gen/ccip/test/MockAdvancedPoolHooks' -import { AccessControl_Data } from '../../../wrappers/gen/ccip/pools/JettonLockBox' import * as CrossChainAddressCodec from '../../../wrappers/ccip/common/CrossChainAddressCodec' import { contractCode } from '../../../wrappers/codeLoader' import { DepositAccount, DepositAccount_ForwardNotification, } from '../../../wrappers/gen/ccip/DepositAccount' +import { runTokenPoolBehaviorTests } from './TokenPool.behavior' +import { runTokenPoolAsyncHookBehaviorTests } from './TokenPool.asyncHook.behavior' +import { runTokenPoolWithdrawFeeTokensBehaviorTests } from './TokenPool.withdrawFeeTokens.behavior' +import { runTokenPoolCcvFeesBehaviorTests } from './TokenPool.ccvFees.behavior' function emptyAccessControlData(): AccessControl_Data { return { @@ -154,7 +153,7 @@ describe('LockReleaseLockboxTokenPool', () => { router: deployer.address, rateLimitAdmin: null, feeAdmin: null, - allowedDepositNamespaces: new Map(), + allowedDepositNamespaces: new Set(), }), jettonClient: JettonClient.create({ masterAddress: jettonMinter.address, @@ -163,9 +162,7 @@ describe('LockReleaseLockboxTokenPool', () => { allowedFinalityConfig: 0n, advancedPoolHooks: null, }), - mirroredPolicy: TokenPool_MirroredPolicy.create({ - onRamps: new Map(), - offRamps: new Map(), + localPolicy: TokenPool_LocalPolicy.create({ cursedSubjects: CursedSubjects.create({ data: new Set(), }), @@ -242,28 +239,6 @@ describe('LockReleaseLockboxTokenPool', () => { success: true, }) - // Update ramp access (deployer is the on-ramp) - const updateRampAccess = await lockReleaseLockboxPool.sendTokenPoolUpdateRampAccess( - deployer.getSender(), - toNano('0.2'), - { - queryId: 2n, - updates: [ - TokenPool_RampUpdate.create({ - remoteChainSelector, - onRamp: deployer.address, - offRamp: offRamp.address, - }), - ], - }, - ) - - expect(updateRampAccess.transactions).toHaveTransaction({ - from: deployer.address, - to: lockReleaseLockboxPool.address, - success: true, - }) - // Mint tokens to deployer's wallet (acts as on-ramp) const mintToDeployer = await jettonMinter.sendMint(deployer.getSender(), { value: toNano('1'), @@ -368,6 +343,7 @@ describe('LockReleaseLockboxTokenPool', () => { 'LockReleaseLockboxTokenPool', async () => ({ pool, + blockchain, deployer, offRamp, unauthorized: recipient, @@ -413,6 +389,7 @@ describe('LockReleaseLockboxTokenPool', () => { return { pool, + blockchain, deployer, offRamp, unauthorized: recipient, @@ -709,7 +686,7 @@ describe('LockReleaseLockboxTokenPool', () => { }) const result = await lockReleaseLockboxPool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.5'), { queryId: 200n, @@ -735,7 +712,7 @@ describe('LockReleaseLockboxTokenPool', () => { // Pool processes the release request expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: lockReleaseLockboxPool.address, success: true, }) @@ -751,7 +728,7 @@ describe('LockReleaseLockboxTokenPool', () => { it('should reject release when requested amount exceeds lockbox liquidity', async () => { const result = await lockReleaseLockboxPool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.4'), { queryId: 201n, @@ -776,7 +753,7 @@ describe('LockReleaseLockboxTokenPool', () => { ) expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: lockReleaseLockboxPool.address, success: false, }) @@ -884,7 +861,7 @@ describe('LockReleaseLockboxTokenPool', () => { // Trigger release from off-ramp const result = await lockReleaseLockboxPool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('1'), { queryId: 400n, @@ -910,7 +887,7 @@ describe('LockReleaseLockboxTokenPool', () => { // Pool processes the release request expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: lockReleaseLockboxPool.address, success: true, }) @@ -950,7 +927,7 @@ describe('LockReleaseLockboxTokenPool', () => { // Trigger release request for more than the lockbox holds const result = await lockReleaseLockboxPool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.5'), { queryId: 401n, @@ -976,7 +953,7 @@ describe('LockReleaseLockboxTokenPool', () => { // The release request should fail or bounce expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: lockReleaseLockboxPool.address, success: false, }) @@ -986,7 +963,7 @@ describe('LockReleaseLockboxTokenPool', () => { }) describe('cursed state', () => { - it('should mirror cursed state locally and block release while cursed', async () => { + it('should apply local cursed state and block release while cursed', async () => { const curseUpdate = await lockReleaseLockboxPool.sendTokenPoolSetCursedSubjects( deployer.getSender(), toNano('0.2'), diff --git a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts index a71d96ba3..4693ea632 100644 --- a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts @@ -11,7 +11,7 @@ import { TokenPool_Data, TokenPool_AdminConfig, TokenPool_DynamicConfig, - TokenPool_MirroredPolicy, + TokenPool_LocalPolicy, TokenPool_ReleaseOrMintFailure, TokenPool_ReleaseOrMintFinished, TokenPool_LockOrBurn, @@ -22,7 +22,6 @@ import { TokenPool_LockOrBurnPrepared, TokenPool_ReleaseOrMintInV1, TokenPool_RateLimitConfigPair, - TokenPool_RampUpdate, TokenPool_ChainUpdate, Ownable2Step, TokenPool_TransferDetails, @@ -122,7 +121,7 @@ describe('LockReleaseTokenPool', () => { router: deployer.address, rateLimitAdmin: null, feeAdmin: null, - allowedDepositNamespaces: new Map(), + allowedDepositNamespaces: new Set(), }), jettonClient: JettonClient.create({ masterAddress: jettonMinter.address, @@ -131,9 +130,7 @@ describe('LockReleaseTokenPool', () => { allowedFinalityConfig: 0n, advancedPoolHooks: null, }), - mirroredPolicy: TokenPool_MirroredPolicy.create({ - onRamps: new Map(), - offRamps: new Map(), + localPolicy: TokenPool_LocalPolicy.create({ cursedSubjects: CursedSubjects.create({ data: new Set(), }), @@ -187,27 +184,6 @@ describe('LockReleaseTokenPool', () => { success: true, }) - const updateRampAccess = await lockReleasePool.sendTokenPoolUpdateRampAccess( - deployer.getSender(), - toNano('0.2'), - { - queryId: 2n, - updates: [ - TokenPool_RampUpdate.create({ - remoteChainSelector, - onRamp: deployer.address, - offRamp: offRamp.address, - }), - ], - }, - ) - - expect(updateRampAccess.transactions).toHaveTransaction({ - from: deployer.address, - to: lockReleasePool.address, - success: true, - }) - const mintToOnRamp = await jettonMinter.sendMint(deployer.getSender(), { value: toNano('1'), message: { @@ -252,6 +228,7 @@ describe('LockReleaseTokenPool', () => { 'LockReleaseTokenPool', async () => ({ pool, + blockchain, deployer, offRamp, unauthorized: recipient, @@ -297,6 +274,7 @@ describe('LockReleaseTokenPool', () => { return { pool, + blockchain, deployer, offRamp, unauthorized: recipient, @@ -686,7 +664,7 @@ describe('LockReleaseTokenPool', () => { it('reverts releaseOrMint when requested amount exceeds pool liquidity', async () => { const result = await lockReleasePool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.4'), { queryId: 46n, @@ -711,7 +689,7 @@ describe('LockReleaseTokenPool', () => { ) expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: lockReleasePool.address, success: false, }) @@ -726,7 +704,7 @@ describe('LockReleaseTokenPool', () => { expect(before.inbound.tokens).toEqual(toNano('100')) const result = await lockReleasePool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.4'), { queryId: 77n, @@ -865,7 +843,7 @@ describe('LockReleaseTokenPool', () => { }) const result = await lockReleasePool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.4'), { queryId: 22n, @@ -890,7 +868,7 @@ describe('LockReleaseTokenPool', () => { ) expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: lockReleasePool.address, success: true, }) @@ -942,7 +920,7 @@ describe('LockReleaseTokenPool', () => { }) const result = await lockReleasePool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.4'), { queryId: 223n, @@ -1007,7 +985,7 @@ describe('LockReleaseTokenPool', () => { expect(await lockReleasePool.getVerifyNotCursed(remoteChainSelector)).toBe(false) const result = await lockReleasePool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), + deployer.getSender(), toNano('0.3'), { queryId: 33n, @@ -1032,7 +1010,7 @@ describe('LockReleaseTokenPool', () => { ) expect(result.transactions).toHaveTransaction({ - from: offRamp.address, + from: deployer.address, to: lockReleasePool.address, success: false, }) diff --git a/contracts/tests/ccip/pools/TokenPool.asyncHook.behavior.ts b/contracts/tests/ccip/pools/TokenPool.asyncHook.behavior.ts index 673effb80..a5554e004 100644 --- a/contracts/tests/ccip/pools/TokenPool.asyncHook.behavior.ts +++ b/contracts/tests/ccip/pools/TokenPool.asyncHook.behavior.ts @@ -100,7 +100,7 @@ export function runTokenPoolAsyncHookBehaviorTests( }), }) const fwdp = TokenPool_ReleaseOrMintForwardPayload.create({ - originalSender: ctx.offRamp.address, + originalSender: ctx.deployer.address, requestMsg: TokenPool_ReleaseOrMint.create({ queryId: 0n, request, @@ -208,7 +208,7 @@ export function runTokenPoolAsyncHookBehaviorTests( const request = releaseRequest(ctx) const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('1'), { queryId: 2n, @@ -241,7 +241,7 @@ export function runTokenPoolAsyncHookBehaviorTests( const request = releaseRequest(ctx) const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('1'), { queryId: 1n, @@ -285,7 +285,7 @@ export function runTokenPoolAsyncHookBehaviorTests( const request = releaseRequest(ctx) const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('1'), { queryId: 3n, @@ -345,7 +345,7 @@ export function runTokenPoolAsyncHookBehaviorTests( const request = releaseRequest(ctx) const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('1'), { queryId: 100n, @@ -356,7 +356,7 @@ export function runTokenPoolAsyncHookBehaviorTests( ) expect(result.transactions).toHaveTransaction({ - from: ctx.offRamp.address, + from: ctx.deployer.address, to: ctx.pool.address, success: true, }) @@ -421,7 +421,7 @@ export function runTokenPoolAsyncHookBehaviorTests( // Even → success const requestEven = releaseRequest(ctx) const evenResult = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('1'), { queryId: 6n, @@ -440,7 +440,7 @@ export function runTokenPoolAsyncHookBehaviorTests( // Odd → failure const requestOdd = releaseRequest(ctx) const oddResult = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('1'), { queryId: 7n, diff --git a/contracts/tests/ccip/pools/TokenPool.behavior.ts b/contracts/tests/ccip/pools/TokenPool.behavior.ts index 68923d923..71985fd4e 100644 --- a/contracts/tests/ccip/pools/TokenPool.behavior.ts +++ b/contracts/tests/ccip/pools/TokenPool.behavior.ts @@ -1,12 +1,11 @@ import '@ton/test-utils' -import { SandboxContract, TreasuryContract } from '@ton/sandbox' +import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox' import { Address, beginCell, Cell, toNano } from '@ton/core' import { CrossChainAddress, CursedSubjects, TokenPool, TokenPool_ChainUpdate, - TokenPool_RampUpdate, TokenPool_RateLimitConfigPair, TokenPool_RateLimitConfigArgs, TokenPool_ReleaseOrMint, @@ -21,6 +20,7 @@ import { export type TokenPoolBehaviorContext = { pool: SandboxContract + blockchain: Blockchain deployer: SandboxContract offRamp: SandboxContract unauthorized: SandboxContract @@ -71,15 +71,13 @@ export function runTokenPoolBehaviorTests( } describe(`${name} TokenPool behavior`, () => { - it('mirrors ramp access and supported chain state after setup', async () => { + it('reports the configured chain as supported after setup', async () => { const ctx = await setup() expect(await ctx.pool.getIsSupportedChain(ctx.remoteChainSelector)).toBe(true) - expect(await ctx.pool.getOnRamp(ctx.remoteChainSelector)).not.toBeNull() - expect(await ctx.pool.getOffRamp(ctx.remoteChainSelector)).toEqualAddress(ctx.offRamp.address) }) - it('reverts releaseOrMint when caller is not configured off-ramp', async () => { + it('reverts releaseOrMint when caller is not the configured Router', async () => { const ctx = await setup() const result = await ctx.pool.sendTokenPoolReleaseOrMint( @@ -97,6 +95,29 @@ export function runTokenPoolBehaviorTests( from: ctx.unauthorized.address, to: ctx.pool.address, success: false, + exitCode: 51710, // TokenPool_Error.Unauthorized + }) + }) + + it('does not authorize an OffRamp to call the pool directly', async () => { + const ctx = await setup() + + const result = await ctx.pool.sendTokenPoolReleaseOrMint( + ctx.offRamp.getSender(), + toNano('0.3'), + { + queryId: 900n, + request: releaseRequest(ctx), + requestedFinalityConfig: 0n, + replyTo: ctx.deployer.address, + }, + ) + + expect(result.transactions).toHaveTransaction({ + from: ctx.offRamp.address, + to: ctx.pool.address, + success: false, + exitCode: 51710, // TokenPool_Error.Unauthorized }) }) @@ -135,7 +156,7 @@ export function runTokenPoolBehaviorTests( expect(await ctx.pool.getVerifyNotCursed(ctx.remoteChainSelector)).toBe(false) const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('0.3'), { queryId: 902n, @@ -146,7 +167,7 @@ export function runTokenPoolBehaviorTests( ) expect(result.transactions).toHaveTransaction({ - from: ctx.offRamp.address, + from: ctx.deployer.address, to: ctx.pool.address, success: false, }) @@ -184,7 +205,7 @@ export function runTokenPoolBehaviorTests( // First release consumes the single inbound token and admits. const first = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('0.3'), { queryId: 931n, @@ -194,14 +215,14 @@ export function runTokenPoolBehaviorTests( }, ) expect(first.transactions).toHaveTransaction({ - from: ctx.offRamp.address, + from: ctx.deployer.address, to: ctx.pool.address, success: true, }) // Second release of the same amount must be rejected (bucket has no refill, rate=0). const second = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('0.3'), { queryId: 932n, @@ -211,19 +232,12 @@ export function runTokenPoolBehaviorTests( }, ) expect(second.transactions).toHaveTransaction({ - from: ctx.offRamp.address, + from: ctx.deployer.address, to: ctx.pool.address, success: false, }) }) - it('returns null ramps for unknown chain', async () => { - const ctx = await setup() - const unknownChainSelector = ctx.remoteChainSelector + 1n - expect(await ctx.pool.getOnRamp(unknownChainSelector)).toBeNull() - expect(await ctx.pool.getOffRamp(unknownChainSelector)).toBeNull() - }) - it('returns unsupported for unknown chain', async () => { const ctx = await setup() const unknownChainSelector = ctx.remoteChainSelector + 1n @@ -249,30 +263,6 @@ export function runTokenPoolBehaviorTests( }) }) - it('rejects updateRampAccess from non-owner', async () => { - const ctx = await setup() - const result = await ctx.pool.sendTokenPoolUpdateRampAccess( - ctx.unauthorized.getSender(), - toNano('0.2'), - { - queryId: 904n, - updates: [ - TokenPool_RampUpdate.create({ - remoteChainSelector: ctx.remoteChainSelector, - onRamp: ctx.onRampAddress, - offRamp: ctx.unauthorized.address, - }), - ], - }, - ) - - expect(result.transactions).toHaveTransaction({ - from: ctx.unauthorized.address, - to: ctx.pool.address, - success: false, - }) - }) - it('rejects cursed-subject updates from non-rmn sender', async () => { const ctx = await setup() const result = await ctx.pool.sendTokenPoolSetCursedSubjects( @@ -306,6 +296,40 @@ export function runTokenPoolBehaviorTests( expect(await ctx.pool.getVerifyNotCursed(ctx.remoteChainSelector)).toBe(true) }) + it('allows the owner to disable RMN proxy updates with addr_none', async () => { + const ctx = await setup() + + await ctx.pool.sendTokenPoolSetRMNProxy(ctx.deployer.getSender(), toNano('0.2'), { + queryId: 903n, + rmnProxy: ctx.unauthorized.address, + }) + + const disable = await ctx.pool.sendTokenPoolSetRMNProxy( + ctx.deployer.getSender(), + toNano('0.2'), + { queryId: 904n, rmnProxy: null }, + ) + expect(disable.transactions).toHaveTransaction({ + from: ctx.deployer.address, + to: ctx.pool.address, + success: true, + }) + + const update = await ctx.pool.sendTokenPoolSetCursedSubjects( + ctx.unauthorized.getSender(), + toNano('0.2'), + { + queryId: 905n, + cursedSubjects: CursedSubjects.create({ data: new Set([ctx.remoteChainSelector]) }), + }, + ) + expect(update.transactions).toHaveTransaction({ + from: ctx.unauthorized.address, + to: ctx.pool.address, + success: false, + }) + }) + it('removes configured chain via applyChainUpdates', async () => { const ctx = await setup() const result = await ctx.pool.sendTokenPoolApplyChainUpdates( @@ -335,7 +359,7 @@ export function runTokenPoolBehaviorTests( }) const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('0.3'), { queryId: 907n, @@ -346,7 +370,7 @@ export function runTokenPoolBehaviorTests( ) expect(result.transactions).toHaveTransaction({ - from: ctx.offRamp.address, + from: ctx.deployer.address, to: ctx.pool.address, success: false, }) @@ -371,64 +395,6 @@ export function runTokenPoolBehaviorTests( }) }) - it('can replace off-ramp mapping via updateRampAccess', async () => { - const ctx = await setup() - const result = await ctx.pool.sendTokenPoolUpdateRampAccess( - ctx.deployer.getSender(), - toNano('0.2'), - { - queryId: 909n, - updates: [ - TokenPool_RampUpdate.create({ - remoteChainSelector: ctx.remoteChainSelector, - onRamp: ctx.onRampAddress, - offRamp: ctx.unauthorized.address, - }), - ], - }, - ) - - expect(result.transactions).toHaveTransaction({ - from: ctx.deployer.address, - to: ctx.pool.address, - success: true, - }) - expect(await ctx.pool.getOffRamp(ctx.remoteChainSelector)).toEqualAddress( - ctx.unauthorized.address, - ) - }) - - it('rejects old off-ramp sender after remapping off-ramp', async () => { - const ctx = await setup() - await ctx.pool.sendTokenPoolUpdateRampAccess(ctx.deployer.getSender(), toNano('0.2'), { - queryId: 910n, - updates: [ - TokenPool_RampUpdate.create({ - remoteChainSelector: ctx.remoteChainSelector, - onRamp: ctx.onRampAddress, - offRamp: ctx.unauthorized.address, - }), - ], - }) - - const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), - toNano('0.3'), - { - queryId: 911n, - request: releaseRequest(ctx), - requestedFinalityConfig: 0n, - replyTo: ctx.deployer.address, - }, - ) - - expect(result.transactions).toHaveTransaction({ - from: ctx.offRamp.address, - to: ctx.pool.address, - success: false, - }) - }) - it('rejects releaseOrMint when source pool is not configured', async () => { const ctx = await setup() const wrongSourcePoolAddress = beginCell() @@ -437,7 +403,7 @@ export function runTokenPoolBehaviorTests( .endCell() .beginParse() const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('0.3'), { queryId: 912n, @@ -448,7 +414,7 @@ export function runTokenPoolBehaviorTests( ) expect(result.transactions).toHaveTransaction({ - from: ctx.offRamp.address, + from: ctx.deployer.address, to: ctx.pool.address, success: false, }) @@ -458,7 +424,7 @@ export function runTokenPoolBehaviorTests( const ctx = await setup() const wrongLocalToken = ctx.deployer.address const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('0.3'), { queryId: 913n, @@ -480,54 +446,7 @@ export function runTokenPoolBehaviorTests( ) expect(result.transactions).toHaveTransaction({ - from: ctx.offRamp.address, - to: ctx.pool.address, - success: false, - }) - }) - - it('clears existing off-ramp when update passes null off-ramp', async () => { - const ctx = await setup() - await ctx.pool.sendTokenPoolUpdateRampAccess(ctx.deployer.getSender(), toNano('0.2'), { - queryId: 914n, - updates: [ - TokenPool_RampUpdate.create({ - remoteChainSelector: ctx.remoteChainSelector, - onRamp: ctx.onRampAddress, - offRamp: null, - }), - ], - }) - - expect(await ctx.pool.getOffRamp(ctx.remoteChainSelector)).toBeNull() - }) - - it('rejects existing off-ramp sender after null off-ramp update', async () => { - const ctx = await setup() - await ctx.pool.sendTokenPoolUpdateRampAccess(ctx.deployer.getSender(), toNano('0.2'), { - queryId: 915n, - updates: [ - TokenPool_RampUpdate.create({ - remoteChainSelector: ctx.remoteChainSelector, - onRamp: ctx.onRampAddress, - offRamp: null, - }), - ], - }) - - const result = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), - toNano('0.3'), - { - queryId: 916n, - request: releaseRequest(ctx), - requestedFinalityConfig: 0n, - replyTo: ctx.deployer.address, - }, - ) - - expect(result.transactions).toHaveTransaction({ - from: ctx.offRamp.address, + from: ctx.deployer.address, to: ctx.pool.address, success: false, }) @@ -602,12 +521,12 @@ export function runTokenPoolBehaviorTests( } const first = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('0.6'), repeatedRequest, ) const second = await ctx.pool.sendTokenPoolReleaseOrMint( - ctx.offRamp.getSender(), + ctx.deployer.getSender(), toNano('0.6'), repeatedRequest, ) diff --git a/contracts/tests/ccip/pools/TokenPool.ccvFees.behavior.ts b/contracts/tests/ccip/pools/TokenPool.ccvFees.behavior.ts index df1cd3844..8c98c3837 100644 --- a/contracts/tests/ccip/pools/TokenPool.ccvFees.behavior.ts +++ b/contracts/tests/ccip/pools/TokenPool.ccvFees.behavior.ts @@ -1,6 +1,6 @@ import '@ton/test-utils' import { Blockchain, SandboxContract, internal } from '@ton/sandbox' -import { Address, beginCell, toNano } from '@ton/core' +import { Address, beginCell, Cell, toNano } from '@ton/core' import { TokenPool, TokenPool_CCVs, @@ -473,7 +473,7 @@ export function runTokenPoolCcvFeesBehaviorTests( TokenPool_QueryCCVsReply.create({ queryId: 23n, requiredCCVs: [ctx.pool.address], - replyPayload: beginCell().endCell(), + replyPayload: Cell.EMPTY, }), ), }), diff --git a/contracts/tests/ccip/router/Router.ccipReceive.spec.ts b/contracts/tests/ccip/router/Router.ccipReceive.spec.ts index 7c888bbf8..3f5424729 100644 --- a/contracts/tests/ccip/router/Router.ccipReceive.spec.ts +++ b/contracts/tests/ccip/router/Router.ccipReceive.spec.ts @@ -120,6 +120,30 @@ describe('Router', () => { }) }) + it('should reject routeMessage while the source chain is cursed', async () => { + await router.sendRouterRMNRemoteCurse(deployer.getSender(), toNano('1'), { + queryId: 4n, + subjects: [any2tvmMessage.sourceChainSelector], + }) + + const result = await router.sendRouterRouteMessage(offRamp.getSender(), toNano('1'), { + message: any2tvmMessage, + execId: genExecID({ + sourceChainSelector: any2tvmMessage.sourceChainSelector, + messageID: 4n, + }), + receiver: receiver.address, + gasLimit: toNano('0.5'), + }) + + expect(result.transactions).toHaveTransaction({ + from: offRamp.address, + to: router.address, + success: false, + exitCode: rt.Router.Errors['Router_Error.SubjectCursed'], + }) + }) + afterAll(async () => { if (process.env['COVERAGE'] === 'true') { await coverage.generateCoverageArtifacts( diff --git a/contracts/tests/ccip/router/Router.cursing.spec.ts b/contracts/tests/ccip/router/Router.cursing.spec.ts index 24ec80db7..2699112e1 100644 --- a/contracts/tests/ccip/router/Router.cursing.spec.ts +++ b/contracts/tests/ccip/router/Router.cursing.spec.ts @@ -19,6 +19,7 @@ describe('Router.cursing', () => { let router: SandboxContract let feeQuoter: SandboxContract let onRamp: SandboxContract + let offRamp: SandboxContract beforeAll(async () => { blockchain = await Blockchain.create() @@ -35,10 +36,11 @@ describe('Router.cursing', () => { } feeQuoter = await blockchain.treasury('feeQuoter') onRamp = await blockchain.treasury('onRamp') + offRamp = await blockchain.treasury('offRamp') }) beforeEach(async () => { - ;({ deployer, sender, router } = await setup(blockchain, { feeQuoter, onRamp })) + ;({ deployer, sender, router } = await setup(blockchain, { feeQuoter, onRamp, offRamp })) }) it('router respects cursing', async () => { @@ -126,6 +128,164 @@ describe('Router.cursing', () => { } }) + it('rejects LockOrBurn through the executor failure channel while cursed', async () => { + const remoteChainSelector = ChainSelectors.testselectors.CHAINSEL_EVM_TEST_90000001 + await router.sendRouterRMNRemoteCurse(deployer.getSender(), toNano('1'), { + queryId: 10n, + subjects: [remoteChainSelector], + }) + + const result = await router.sendRouterLockOrBurn(onRamp.getSender(), toNano('1'), { + queryID: 11n, + tokenPool: deployer.address, + request: rt.TokenPool_LockOrBurnInV1.create({ + transfer: rt.TokenPool_Transfer.create({ + id: 11n, + details: rt.TokenPool_TransferDetails.create({ + originalSender: sender.address, + remoteChainSelector, + receiver: EVM_ADDRESS, + amount: 1n, + localToken: sender.address, + }), + }), + }), + executorAddress: sender.address, + }) + + expect(result.transactions).toHaveTransaction({ + from: onRamp.address, + to: router.address, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: router.address, + to: sender.address, + success: true, + op: 0xb76e3a84, // Router_TokenPoolLockOrBurnFailed + }) + expect(result.transactions).not.toHaveTransaction({ + from: router.address, + to: deployer.address, + }) + }) + + it('rejects ReleaseOrMint back to the OffRamp while the source lane is cursed', async () => { + const sourceChainSelector = ChainSelectors.testselectors.CHAINSEL_EVM_TEST_90000001 + await router.sendRouterRMNRemoteCurse(deployer.getSender(), toNano('1'), { + queryId: 20n, + subjects: [sourceChainSelector], + }) + + const result = await router.sendRouterRelayReleaseOrMint(offRamp.getSender(), toNano('1'), { + queryID: 21n, + sourceChainSelector, + tokenPool: deployer.address, + request: rt.TokenPool_ReleaseOrMintInV1.create({ + transfer: rt.TokenPool_Transfer.create({ + id: 21n, + details: rt.TokenPool_TransferDetails.create({ + originalSender: EVM_ADDRESS, + remoteChainSelector: sourceChainSelector, + receiver: sender.address, + amount: 1n, + localToken: sender.address, + }), + }), + sourcePoolAddress: EVM_ADDRESS, + sourcePoolData: null, + offchainTokenData: null, + }), + requestedFinalityConfig: 0n, + replyTo: sender.address, + }) + + expect(result.transactions).toHaveTransaction({ + from: offRamp.address, + to: router.address, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: router.address, + to: offRamp.address, + success: true, + op: rt.Router_TokenPoolReleaseOrMintFailed.PREFIX, + }) + expect(result.transactions).not.toHaveTransaction({ + from: router.address, + to: deployer.address, + }) + }) + + it('rejects a ReleaseOrMint relay from an unregistered OffRamp', async () => { + const sourceChainSelector = ChainSelectors.testselectors.CHAINSEL_EVM_TEST_90000001 + + const result = await router.sendRouterRelayReleaseOrMint(sender.getSender(), toNano('1'), { + queryID: 30n, + sourceChainSelector, + tokenPool: deployer.address, + request: rt.TokenPool_ReleaseOrMintInV1.create({ + transfer: rt.TokenPool_Transfer.create({ + id: 30n, + details: rt.TokenPool_TransferDetails.create({ + originalSender: EVM_ADDRESS, + remoteChainSelector: sourceChainSelector, + receiver: sender.address, + amount: 1n, + localToken: sender.address, + }), + }), + sourcePoolAddress: EVM_ADDRESS, + sourcePoolData: null, + offchainTokenData: null, + }), + requestedFinalityConfig: 0n, + replyTo: sender.address, + }) + + expect(result.transactions).toHaveTransaction({ + from: sender.address, + to: router.address, + success: false, + exitCode: rt.Router.Errors['Router_Error.SenderIsNotOffRamp'], + }) + }) + + it('rejects a ReleaseOrMint relay whose request selector mismatches the source chain', async () => { + const sourceChainSelector = ChainSelectors.testselectors.CHAINSEL_EVM_TEST_90000001 + + const result = await router.sendRouterRelayReleaseOrMint(offRamp.getSender(), toNano('1'), { + queryID: 31n, + sourceChainSelector, + tokenPool: deployer.address, + request: rt.TokenPool_ReleaseOrMintInV1.create({ + transfer: rt.TokenPool_Transfer.create({ + id: 31n, + details: rt.TokenPool_TransferDetails.create({ + // Mismatched remote chain selector. + originalSender: EVM_ADDRESS, + remoteChainSelector: sourceChainSelector + 1n, + receiver: sender.address, + amount: 1n, + localToken: sender.address, + }), + }), + sourcePoolAddress: EVM_ADDRESS, + sourcePoolData: null, + offchainTokenData: null, + }), + requestedFinalityConfig: 0n, + replyTo: sender.address, + }) + + expect(result.transactions).toHaveTransaction({ + from: offRamp.address, + to: router.address, + success: false, + exitCode: rt.Router.Errors['Router_Error.SourceChainSelectorMismatch'], + }) + }) + it('router respect global cursing', async () => { // Curse all lanes { diff --git a/contracts/wrappers/gen/ccip/CCIPSendExecutor.ts b/contracts/wrappers/gen/ccip/CCIPSendExecutor.ts index 5de63ba50..47305b12f 100644 --- a/contracts/wrappers/gen/ccip/CCIPSendExecutor.ts +++ b/contracts/wrappers/gen/ccip/CCIPSendExecutor.ts @@ -160,6 +160,8 @@ function invokeCustomUnpackFromSlice(typeName: string, s: c.Slice): T { type coins = bigint +type int32 = bigint + type uint16 = bigint type uint32 = bigint type uint64 = bigint @@ -533,6 +535,49 @@ export const TokenPool_LockOrBurnFinished = { } } +/** + > struct (0x3476ea72) TokenPool_LockOrBurnFailure { + > queryId: uint64 + > errorCode: uint16 + > } + */ +export interface TokenPool_LockOrBurnFailure { + readonly $: 'TokenPool_LockOrBurnFailure' + queryId: uint64 + errorCode: uint16 +} + +export const TokenPool_LockOrBurnFailure = { + PREFIX: 0x3476ea72, + + create(args: { + queryId?: uint64 + errorCode: uint16 + }): TokenPool_LockOrBurnFailure { + return { + $: 'TokenPool_LockOrBurnFailure', + ...args, + queryId: args.queryId ?? 0n + } + }, + fromSlice(s: c.Slice): TokenPool_LockOrBurnFailure { + loadAndCheckPrefix32(s, 0x3476ea72, 'TokenPool_LockOrBurnFailure'); + return { + $: 'TokenPool_LockOrBurnFailure', + queryId: s.loadUintBig(64), + errorCode: s.loadUintBig(16), + } + }, + store(self: TokenPool_LockOrBurnFailure, b: c.Builder): void { + b.storeUint(0x3476ea72, 32); + b.storeUint(self.queryId, 64); + b.storeUint(self.errorCode, 16); + }, + toCell(self: TokenPool_LockOrBurnFailure): c.Cell { + return makeCellFrom(self, TokenPool_LockOrBurnFailure.store); + } +} + /** > struct TokenPool_LockOrBurnPrepared { > feeAmount: coins @@ -1439,6 +1484,54 @@ export const Router_WithdrawRequest = { } } +/** + > struct (0xb76e3a84) Router_TokenPoolLockOrBurnFailed { + > queryID: uint64 + > tokenPool: address + > exitCode: int32 + > } + */ +export interface Router_TokenPoolLockOrBurnFailed { + readonly $: 'Router_TokenPoolLockOrBurnFailed' + queryID: uint64 + tokenPool: c.Address + exitCode: int32 +} + +export const Router_TokenPoolLockOrBurnFailed = { + PREFIX: 0xb76e3a84, + + create(args: { + queryID?: uint64 + tokenPool: c.Address + exitCode: int32 + }): Router_TokenPoolLockOrBurnFailed { + return { + $: 'Router_TokenPoolLockOrBurnFailed', + ...args, + queryID: args.queryID ?? 0n + } + }, + fromSlice(s: c.Slice): Router_TokenPoolLockOrBurnFailed { + loadAndCheckPrefix32(s, 0xb76e3a84, 'Router_TokenPoolLockOrBurnFailed'); + return { + $: 'Router_TokenPoolLockOrBurnFailed', + queryID: s.loadUintBig(64), + tokenPool: s.loadAddress(), + exitCode: s.loadIntBig(32), + } + }, + store(self: Router_TokenPoolLockOrBurnFailed, b: c.Builder): void { + b.storeUint(0xb76e3a84, 32); + b.storeUint(self.queryID, 64); + b.storeAddress(self.tokenPool); + b.storeInt(self.exitCode, 32); + }, + toCell(self: Router_TokenPoolLockOrBurnFailed): c.Cell { + return makeCellFrom(self, Router_TokenPoolLockOrBurnFailed.store); + } +} + /** > type CrossChainAddress = slice */ @@ -2289,7 +2382,7 @@ export const CCIPSendExecutor_Execute = { } /** - > enum CCIPSendExecutor_Error { 6 variants } + > enum CCIPSendExecutor_Error { 7 variants } */ export type CCIPSendExecutor_Error = bigint @@ -2300,6 +2393,7 @@ export const CCIPSendExecutor_Error = { InsufficientFee: 17803n, FeeQuoterBounce: 17804n, TokenNotEnabled: 17805n, + TokenPoolBounce: 17806n, fromSlice(s: c.Slice): CCIPSendExecutor_Error { return s.loadUintBig(15); @@ -2351,7 +2445,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class CCIPSendExecutor implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Utils_Error.InvalidData': 13500, @@ -2362,6 +2456,7 @@ export class CCIPSendExecutor implements c.Contract { 'CCIPSendExecutor_Error.InsufficientFee': 17803, 'CCIPSendExecutor_Error.FeeQuoterBounce': 17804, 'CCIPSendExecutor_Error.TokenNotEnabled': 17805, + 'CCIPSendExecutor_Error.TokenPoolBounce': 17806, } readonly address: c.Address @@ -2446,6 +2541,21 @@ export class CCIPSendExecutor implements c.Contract { return TokenPool_LockOrBurnFinished.toCell(TokenPool_LockOrBurnFinished.create(body)); } + static createCellOfTokenPoolLockOrBurnFailure(body: { + queryId?: uint64 + errorCode: uint16 + }) { + return TokenPool_LockOrBurnFailure.toCell(TokenPool_LockOrBurnFailure.create(body)); + } + + static createCellOfRouterTokenPoolLockOrBurnFailed(body: { + queryID?: uint64 + tokenPool: c.Address + exitCode: int32 + }) { + return Router_TokenPoolLockOrBurnFailed.toCell(Router_TokenPoolLockOrBurnFailed.create(body)); + } + async sendDeploy(provider: ContractProvider, via: Sender, msgValue: coins, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -2537,6 +2647,29 @@ export class CCIPSendExecutor implements c.Contract { }); } + async sendTokenPoolLockOrBurnFailure(provider: ContractProvider, via: Sender, msgValue: coins, body: { + queryId?: uint64 + errorCode: uint16 + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: TokenPool_LockOrBurnFailure.toCell(TokenPool_LockOrBurnFailure.create(body)), + ...extraOptions + }); + } + + async sendRouterTokenPoolLockOrBurnFailed(provider: ContractProvider, via: Sender, msgValue: coins, body: { + queryID?: uint64 + tokenPool: c.Address + exitCode: int32 + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: Router_TokenPoolLockOrBurnFailed.toCell(Router_TokenPoolLockOrBurnFailed.create(body)), + ...extraOptions + }); + } + async getTypeAndVersion(provider: ContractProvider): Promise<[ c.Slice, c.Slice, diff --git a/contracts/wrappers/gen/ccip/OffRamp.ts b/contracts/wrappers/gen/ccip/OffRamp.ts index 03fcd06c5..836f42f4b 100644 --- a/contracts/wrappers/gen/ccip/OffRamp.ts +++ b/contracts/wrappers/gen/ccip/OffRamp.ts @@ -1327,60 +1327,6 @@ export const Any2TVMMessage = { } } -/** - > struct (0x351f77e3) TokenPool_ReleaseOrMint { - > queryId: uint64 - > request: Cell - > requestedFinalityConfig: uint32 - > replyTo: address? - > } - */ -export interface TokenPool_ReleaseOrMint { - readonly $: 'TokenPool_ReleaseOrMint' - queryId: uint64 - request: TokenPool_ReleaseOrMintInV1 - requestedFinalityConfig: uint32 - replyTo: c.Address | null /* = null */ -} - -export const TokenPool_ReleaseOrMint = { - PREFIX: 0x351f77e3, - - create(args: { - queryId?: uint64 - request: TokenPool_ReleaseOrMintInV1 - requestedFinalityConfig: uint32 - replyTo?: c.Address | null /* = null */ - }): TokenPool_ReleaseOrMint { - return { - $: 'TokenPool_ReleaseOrMint', - replyTo: null, - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_ReleaseOrMint { - loadAndCheckPrefix32(s, 0x351f77e3, 'TokenPool_ReleaseOrMint'); - return { - $: 'TokenPool_ReleaseOrMint', - queryId: s.loadUintBig(64), - request: loadCellRef(s, TokenPool_ReleaseOrMintInV1.fromSlice), - requestedFinalityConfig: s.loadUintBig(32), - replyTo: s.loadMaybeAddress(), - } - }, - store(self: TokenPool_ReleaseOrMint, b: c.Builder): void { - b.storeUint(0x351f77e3, 32); - b.storeUint(self.queryId, 64); - storeCellRef(self.request, b, TokenPool_ReleaseOrMintInV1.store); - b.storeUint(self.requestedFinalityConfig, 32); - b.storeAddress(self.replyTo); - }, - toCell(self: TokenPool_ReleaseOrMint): c.Cell { - return makeCellFrom(self, TokenPool_ReleaseOrMint.store); - } -} - /** > type CrossChainAddress = slice */ @@ -2074,6 +2020,127 @@ export const Router_RouteMessage = { } } +/** + > struct (0xb19862a3) Router_RelayReleaseOrMint { + > queryID: uint64 + > sourceChainSelector: uint64 + > tokenPool: address + > request: Cell + > requestedFinalityConfig: uint32 + > replyTo: address + > } + */ +export interface Router_RelayReleaseOrMint { + readonly $: 'Router_RelayReleaseOrMint' + queryID: uint64 + sourceChainSelector: uint64 + tokenPool: c.Address + request: TokenPool_ReleaseOrMintInV1 + requestedFinalityConfig: uint32 + replyTo: c.Address +} + +export const Router_RelayReleaseOrMint = { + PREFIX: 0xb19862a3, + + create(args: { + queryID?: uint64 + sourceChainSelector: uint64 + tokenPool: c.Address + request: TokenPool_ReleaseOrMintInV1 + requestedFinalityConfig: uint32 + replyTo: c.Address + }): Router_RelayReleaseOrMint { + return { + $: 'Router_RelayReleaseOrMint', + ...args, + queryID: args.queryID ?? 0n + } + }, + fromSlice(s: c.Slice): Router_RelayReleaseOrMint { + loadAndCheckPrefix32(s, 0xb19862a3, 'Router_RelayReleaseOrMint'); + return { + $: 'Router_RelayReleaseOrMint', + queryID: s.loadUintBig(64), + sourceChainSelector: s.loadUintBig(64), + tokenPool: s.loadAddress(), + request: loadCellRef(s, TokenPool_ReleaseOrMintInV1.fromSlice), + requestedFinalityConfig: s.loadUintBig(32), + replyTo: s.loadAddress(), + } + }, + store(self: Router_RelayReleaseOrMint, b: c.Builder): void { + b.storeUint(0xb19862a3, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.sourceChainSelector, 64); + b.storeAddress(self.tokenPool); + storeCellRef(self.request, b, TokenPool_ReleaseOrMintInV1.store); + b.storeUint(self.requestedFinalityConfig, 32); + b.storeAddress(self.replyTo); + }, + toCell(self: Router_RelayReleaseOrMint): c.Cell { + return makeCellFrom(self, Router_RelayReleaseOrMint.store); + } +} + +/** + > struct (0xb980cb4e) Router_TokenPoolReleaseOrMintFailed { + > queryID: uint64 + > sourceChainSelector: uint64 + > replyTo: address + > tokenPool: address + > exitCode: int32 + > } + */ +export interface Router_TokenPoolReleaseOrMintFailed { + readonly $: 'Router_TokenPoolReleaseOrMintFailed' + queryID: uint64 + sourceChainSelector: uint64 + replyTo: c.Address + tokenPool: c.Address + exitCode: int32 +} + +export const Router_TokenPoolReleaseOrMintFailed = { + PREFIX: 0xb980cb4e, + + create(args: { + queryID?: uint64 + sourceChainSelector: uint64 + replyTo: c.Address + tokenPool: c.Address + exitCode: int32 + }): Router_TokenPoolReleaseOrMintFailed { + return { + $: 'Router_TokenPoolReleaseOrMintFailed', + ...args, + queryID: args.queryID ?? 0n + } + }, + fromSlice(s: c.Slice): Router_TokenPoolReleaseOrMintFailed { + loadAndCheckPrefix32(s, 0xb980cb4e, 'Router_TokenPoolReleaseOrMintFailed'); + return { + $: 'Router_TokenPoolReleaseOrMintFailed', + queryID: s.loadUintBig(64), + sourceChainSelector: s.loadUintBig(64), + replyTo: s.loadAddress(), + tokenPool: s.loadAddress(), + exitCode: s.loadIntBig(32), + } + }, + store(self: Router_TokenPoolReleaseOrMintFailed, b: c.Builder): void { + b.storeUint(0xb980cb4e, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.sourceChainSelector, 64); + b.storeAddress(self.replyTo); + b.storeAddress(self.tokenPool); + b.storeInt(self.exitCode, 32); + }, + toCell(self: Router_TokenPoolReleaseOrMintFailed): c.Cell { + return makeCellFrom(self, Router_TokenPoolReleaseOrMintFailed.store); + } +} + /** > struct (0x9d431905) OffRamp_Commit { > queryId: uint64 @@ -4259,7 +4326,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class OffRamp implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'MerkleMultiProof_Error.InvalidProofLeavesCannotBeEmpty': 12000, @@ -4437,6 +4504,16 @@ export class OffRamp implements c.Contract { return OffRamp_CCIPReceiveBounced.toCell(OffRamp_CCIPReceiveBounced.create(body)); } + static createCellOfRouterTokenPoolReleaseOrMintFailed(body: { + queryID?: uint64 + sourceChainSelector: uint64 + replyTo: c.Address + tokenPool: c.Address + exitCode: int32 + }) { + return Router_TokenPoolReleaseOrMintFailed.toCell(Router_TokenPoolReleaseOrMintFailed.create(body)); + } + static createCellOfOffRampNotifyFailure(body: { queryId?: uint64 header: RampMessageHeader @@ -4642,6 +4719,20 @@ export class OffRamp implements c.Contract { }); } + async sendRouterTokenPoolReleaseOrMintFailed(provider: ContractProvider, via: Sender, msgValue: coins, body: { + queryID?: uint64 + sourceChainSelector: uint64 + replyTo: c.Address + tokenPool: c.Address + exitCode: int32 + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: Router_TokenPoolReleaseOrMintFailed.toCell(Router_TokenPoolReleaseOrMintFailed.create(body)), + ...extraOptions + }); + } + async sendOffRampNotifyFailure(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 header: RampMessageHeader diff --git a/contracts/wrappers/gen/ccip/OnRamp.ts b/contracts/wrappers/gen/ccip/OnRamp.ts index 8ae39b25a..c0345ea0a 100644 --- a/contracts/wrappers/gen/ccip/OnRamp.ts +++ b/contracts/wrappers/gen/ccip/OnRamp.ts @@ -3413,7 +3413,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class OnRamp implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'OnRamp_Error.UnknownDestChainSelector': 13400, diff --git a/contracts/wrappers/gen/ccip/Router.ts b/contracts/wrappers/gen/ccip/Router.ts index a14120ee6..50160bc10 100644 --- a/contracts/wrappers/gen/ccip/Router.ts +++ b/contracts/wrappers/gen/ccip/Router.ts @@ -267,6 +267,8 @@ function invokeCustomUnpackFromSlice(typeName: string, s: c.Slice): T { type coins = bigint +type int32 = bigint + type uint16 = bigint type uint32 = bigint type uint64 = bigint @@ -1017,6 +1019,247 @@ export const CursedSubjects = { } } +/** + > type CrossChainAddress = slice + */ +export type CrossChainAddress = c.Slice + +export const CrossChainAddress = { + fromSlice(s: c.Slice): CrossChainAddress { + return invokeCustomUnpackFromSlice('CrossChainAddress', s); + }, + store(self: CrossChainAddress, b: c.Builder): void { + invokeCustomPackToBuilder('CrossChainAddress', self, b); + }, + toCell(self: CrossChainAddress): c.Cell { + return makeCellFrom(self, CrossChainAddress.store); + } +} + +/** + > type ExtraArgs = GenericExtraArgsV2 | SVMExtraArgsV1 | SuiExtraArgsV1 + */ +export type ExtraArgs = + | GenericExtraArgsV2 + | SVMExtraArgsV1 + | SuiExtraArgsV1 + +export const ExtraArgs = { + fromSlice(s: c.Slice): ExtraArgs { + return lookupPrefix(s, 0x181dcf10, 32) ? GenericExtraArgsV2.fromSlice(s) : + lookupPrefix(s, 0x1f3b3aba, 32) ? SVMExtraArgsV1.fromSlice(s) : + lookupPrefix(s, 0x21ea4ca9, 32) ? SuiExtraArgsV1.fromSlice(s) : + throwNonePrefixMatch('ExtraArgs'); + }, + store(self: ExtraArgs, b: c.Builder): void { + switch (self.$) { + case 'GenericExtraArgsV2': + GenericExtraArgsV2.store(self, b); + break; + case 'SVMExtraArgsV1': + SVMExtraArgsV1.store(self, b); + break; + case 'SuiExtraArgsV1': + SuiExtraArgsV1.store(self, b); + break; + } + }, + toCell(self: ExtraArgs): c.Cell { + return makeCellFrom(self, ExtraArgs.store); + } +} + +/** + > struct (0x181dcf10) GenericExtraArgsV2 { + > gasLimit: uint256? + > allowOutOfOrderExecution: bool + > } + */ +export interface GenericExtraArgsV2 { + readonly $: 'GenericExtraArgsV2' + gasLimit: uint256 | null + allowOutOfOrderExecution: boolean +} + +export const GenericExtraArgsV2 = { + PREFIX: 0x181dcf10, + + create(args: { + gasLimit: uint256 | null + allowOutOfOrderExecution: boolean + }): GenericExtraArgsV2 { + return { + $: 'GenericExtraArgsV2', + ...args + } + }, + fromSlice(s: c.Slice): GenericExtraArgsV2 { + loadAndCheckPrefix32(s, 0x181dcf10, 'GenericExtraArgsV2'); + return { + $: 'GenericExtraArgsV2', + gasLimit: s.loadBoolean() ? s.loadUintBig(256) : null, + allowOutOfOrderExecution: s.loadBoolean(), + } + }, + store(self: GenericExtraArgsV2, b: c.Builder): void { + b.storeUint(0x181dcf10, 32); + storeTolkNullable(self.gasLimit, b, + (v,b) => b.storeUint(v, 256) + ); + b.storeBit(self.allowOutOfOrderExecution); + }, + toCell(self: GenericExtraArgsV2): c.Cell { + return makeCellFrom(self, GenericExtraArgsV2.store); + } +} + +/** + > struct (0x1f3b3aba) SVMExtraArgsV1 { + > computeUnits: uint32 + > accountIsWritableBitmap: uint64 + > allowOutOfOrderExecution: bool + > tokenReceiver: uint256 + > accounts: SnakedCell + > } + */ +export interface SVMExtraArgsV1 { + readonly $: 'SVMExtraArgsV1' + computeUnits: uint32 + accountIsWritableBitmap: uint64 + allowOutOfOrderExecution: boolean + tokenReceiver: uint256 + accounts: SnakedCell +} + +export const SVMExtraArgsV1 = { + PREFIX: 0x1f3b3aba, + + create(args: { + computeUnits: uint32 + accountIsWritableBitmap: uint64 + allowOutOfOrderExecution: boolean + tokenReceiver: uint256 + accounts: SnakedCell + }): SVMExtraArgsV1 { + return { + $: 'SVMExtraArgsV1', + ...args + } + }, + fromSlice(s: c.Slice): SVMExtraArgsV1 { + loadAndCheckPrefix32(s, 0x1f3b3aba, 'SVMExtraArgsV1'); + return { + $: 'SVMExtraArgsV1', + computeUnits: s.loadUintBig(32), + accountIsWritableBitmap: s.loadUintBig(64), + allowOutOfOrderExecution: s.loadBoolean(), + tokenReceiver: s.loadUintBig(256), + accounts: loadSnakedCellOf(s, (s) => s.loadUintBig(256)), + } + }, + store(self: SVMExtraArgsV1, b: c.Builder): void { + b.storeUint(0x1f3b3aba, 32); + b.storeUint(self.computeUnits, 32); + b.storeUint(self.accountIsWritableBitmap, 64); + b.storeBit(self.allowOutOfOrderExecution); + b.storeUint(self.tokenReceiver, 256); + storeSnakedCellOf(self.accounts, b, (v, b) => b.storeUint(v, 256)); + }, + toCell(self: SVMExtraArgsV1): c.Cell { + return makeCellFrom(self, SVMExtraArgsV1.store); + } +} + +/** + > struct (0x21ea4ca9) SuiExtraArgsV1 { + > gasLimit: uint256 + > allowOutOfOrderExecution: bool + > tokenReceiver: uint256 + > receiverObjectIds: SnakedCell + > } + */ +export interface SuiExtraArgsV1 { + readonly $: 'SuiExtraArgsV1' + gasLimit: uint256 + allowOutOfOrderExecution: boolean + tokenReceiver: uint256 + receiverObjectIds: SnakedCell +} + +export const SuiExtraArgsV1 = { + PREFIX: 0x21ea4ca9, + + create(args: { + gasLimit: uint256 + allowOutOfOrderExecution: boolean + tokenReceiver: uint256 + receiverObjectIds: SnakedCell + }): SuiExtraArgsV1 { + return { + $: 'SuiExtraArgsV1', + ...args + } + }, + fromSlice(s: c.Slice): SuiExtraArgsV1 { + loadAndCheckPrefix32(s, 0x21ea4ca9, 'SuiExtraArgsV1'); + return { + $: 'SuiExtraArgsV1', + gasLimit: s.loadUintBig(256), + allowOutOfOrderExecution: s.loadBoolean(), + tokenReceiver: s.loadUintBig(256), + receiverObjectIds: loadSnakedCellOf(s, (s) => s.loadUintBig(256)), + } + }, + store(self: SuiExtraArgsV1, b: c.Builder): void { + b.storeUint(0x21ea4ca9, 32); + b.storeUint(self.gasLimit, 256); + b.storeBit(self.allowOutOfOrderExecution); + b.storeUint(self.tokenReceiver, 256); + storeSnakedCellOf(self.receiverObjectIds, b, (v, b) => b.storeUint(v, 256)); + }, + toCell(self: SuiExtraArgsV1): c.Cell { + return makeCellFrom(self, SuiExtraArgsV1.store); + } +} + +/** + > struct TokenAmount { + > amount: coins + > token: address + > } + */ +export interface TokenAmount { + readonly $: 'TokenAmount' + amount: coins + token: c.Address +} + +export const TokenAmount = { + create(args: { + amount: coins + token: c.Address + }): TokenAmount { + return { + $: 'TokenAmount', + ...args + } + }, + fromSlice(s: c.Slice): TokenAmount { + return { + $: 'TokenAmount', + amount: s.loadCoins(), + token: s.loadAddress(), + } + }, + store(self: TokenAmount, b: c.Builder): void { + b.storeCoins(self.amount); + b.storeAddress(self.token); + }, + toCell(self: TokenAmount): c.Cell { + return makeCellFrom(self, TokenAmount.store); + } +} + /** > struct (0xdcf993c2) OnRamp_Send { > msg: Cell @@ -1429,1912 +1672,2036 @@ export const TokenPool_LockOrBurnForwardPayload = { } /** - > struct OnRamps { - > destChainSelectors: SnakedCell - > onRamp: address? + > struct (0x351f77e3) TokenPool_ReleaseOrMint { + > queryId: uint64 + > request: Cell + > requestedFinalityConfig: uint32 + > replyTo: address? > } */ -export interface OnRamps { - readonly $: 'OnRamps' - destChainSelectors: SnakedCell - onRamp: c.Address | null +export interface TokenPool_ReleaseOrMint { + readonly $: 'TokenPool_ReleaseOrMint' + queryId: uint64 + request: TokenPool_ReleaseOrMintInV1 + requestedFinalityConfig: uint32 + replyTo: c.Address | null /* = null */ } -export const OnRamps = { +export const TokenPool_ReleaseOrMint = { + PREFIX: 0x351f77e3, + create(args: { - destChainSelectors: SnakedCell - onRamp: c.Address | null - }): OnRamps { + queryId?: uint64 + request: TokenPool_ReleaseOrMintInV1 + requestedFinalityConfig: uint32 + replyTo?: c.Address | null /* = null */ + }): TokenPool_ReleaseOrMint { return { - $: 'OnRamps', - ...args + $: 'TokenPool_ReleaseOrMint', + replyTo: null, + ...args, + queryId: args.queryId ?? 0n } }, - fromSlice(s: c.Slice): OnRamps { + fromSlice(s: c.Slice): TokenPool_ReleaseOrMint { + loadAndCheckPrefix32(s, 0x351f77e3, 'TokenPool_ReleaseOrMint'); return { - $: 'OnRamps', - destChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), - onRamp: s.loadMaybeAddress(), + $: 'TokenPool_ReleaseOrMint', + queryId: s.loadUintBig(64), + request: loadCellRef(s, TokenPool_ReleaseOrMintInV1.fromSlice), + requestedFinalityConfig: s.loadUintBig(32), + replyTo: s.loadMaybeAddress(), } }, - store(self: OnRamps, b: c.Builder): void { - storeSnakedCellOf(self.destChainSelectors, b, (v, b) => b.storeUint(v, 64)); - b.storeAddress(self.onRamp); + store(self: TokenPool_ReleaseOrMint, b: c.Builder): void { + b.storeUint(0x351f77e3, 32); + b.storeUint(self.queryId, 64); + storeCellRef(self.request, b, TokenPool_ReleaseOrMintInV1.store); + b.storeUint(self.requestedFinalityConfig, 32); + b.storeAddress(self.replyTo); }, - toCell(self: OnRamps): c.Cell { - return makeCellFrom(self, OnRamps.store); + toCell(self: TokenPool_ReleaseOrMint): c.Cell { + return makeCellFrom(self, TokenPool_ReleaseOrMint.store); } } /** - > struct OffRamps { - > sourceChainSelectors: SnakedCell - > offRamp: address + > struct TokenPool_LockOrBurnPrepared { + > feeAmount: coins + > destTokenAmount: coins + > out: TokenPool_LockOrBurnOutV1 > } */ -export interface OffRamps { - readonly $: 'OffRamps' - sourceChainSelectors: SnakedCell - offRamp: c.Address +export interface TokenPool_LockOrBurnPrepared { + readonly $: 'TokenPool_LockOrBurnPrepared' + feeAmount: coins + destTokenAmount: coins + out: TokenPool_LockOrBurnOutV1 } -export const OffRamps = { +export const TokenPool_LockOrBurnPrepared = { create(args: { - sourceChainSelectors: SnakedCell - offRamp: c.Address - }): OffRamps { + feeAmount: coins + destTokenAmount: coins + out: TokenPool_LockOrBurnOutV1 + }): TokenPool_LockOrBurnPrepared { return { - $: 'OffRamps', + $: 'TokenPool_LockOrBurnPrepared', ...args } }, - fromSlice(s: c.Slice): OffRamps { + fromSlice(s: c.Slice): TokenPool_LockOrBurnPrepared { return { - $: 'OffRamps', - sourceChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), - offRamp: s.loadAddress(), + $: 'TokenPool_LockOrBurnPrepared', + feeAmount: s.loadCoins(), + destTokenAmount: s.loadCoins(), + out: TokenPool_LockOrBurnOutV1.fromSlice(s), } }, - store(self: OffRamps, b: c.Builder): void { - storeSnakedCellOf(self.sourceChainSelectors, b, (v, b) => b.storeUint(v, 64)); - b.storeAddress(self.offRamp); + store(self: TokenPool_LockOrBurnPrepared, b: c.Builder): void { + b.storeCoins(self.feeAmount); + b.storeCoins(self.destTokenAmount); + TokenPool_LockOrBurnOutV1.store(self.out, b); }, - toCell(self: OffRamps): c.Cell { - return makeCellFrom(self, OffRamps.store); + toCell(self: TokenPool_LockOrBurnPrepared): c.Cell { + return makeCellFrom(self, TokenPool_LockOrBurnPrepared.store); } } /** - > type Router_GetValidatedFee_Any = Router_GetValidatedFee + > struct TokenPool_Transfer { + > id: uint256 + > details: Cell> + > } */ -export type Router_GetValidatedFee_Any = Router_GetValidatedFee +export interface TokenPool_Transfer { + readonly $: 'TokenPool_Transfer' + id: uint256 + details: TokenPool_TransferDetails +} -export const Router_GetValidatedFee_Any = { - fromSlice(s: c.Slice): Router_GetValidatedFee_Any { - return (() => { - loadAndCheckPrefix32(s, 0x4dd6aa82, 'Router_GetValidatedFee'); - return { - $: 'Router_GetValidatedFee', - ccipSend: loadCellRef(s, Router_CCIPSend.fromSlice), - context: loadTolkRemaining(s), - } - })(); +export const TokenPool_Transfer = { + create(args: { + id: uint256 + details: TokenPool_TransferDetails + }): TokenPool_Transfer { + return { + $: 'TokenPool_Transfer', + ...args + } }, - store(self: Router_GetValidatedFee_Any, b: c.Builder): void { - b.storeUint(0x4dd6aa82, 32); - storeCellRef(self.ccipSend, b, Router_CCIPSend.store); - storeTolkRemaining(self.context, b); +} + +/** + > struct TokenPool_TransferDetails { + > receiver: R + > remoteChainSelector: uint64 + > originalSender: S + > amount: C + > localToken: address + > } + */ +export interface TokenPool_TransferDetails { + readonly $: 'TokenPool_TransferDetails' + receiver: R + remoteChainSelector: uint64 + originalSender: S + amount: C + localToken: c.Address +} + +export const TokenPool_TransferDetails = { + create(args: { + receiver: R + remoteChainSelector: uint64 + originalSender: S + amount: C + localToken: c.Address + }): TokenPool_TransferDetails { + return { + $: 'TokenPool_TransferDetails', + ...args + } }, - toCell(self: Router_GetValidatedFee_Any): c.Cell { - return makeCellFrom(self, Router_GetValidatedFee_Any.store); - } } /** - > type OnRamp_MessageValidated_GetValidatedFeeContext = OnRamp_MessageValidated + > type TokenPool_LockOrBurnTransfer = TokenPool_Transfer, coins> */ -export type OnRamp_MessageValidated_GetValidatedFeeContext = OnRamp_MessageValidated +export type TokenPool_LockOrBurnTransfer = TokenPool_Transfer -export const OnRamp_MessageValidated_GetValidatedFeeContext = { - fromSlice(s: c.Slice): OnRamp_MessageValidated_GetValidatedFeeContext { +export const TokenPool_LockOrBurnTransfer = { + fromSlice(s: c.Slice): TokenPool_LockOrBurnTransfer { return (() => { - loadAndCheckPrefix32(s, 0x2afb11bd, 'OnRamp_MessageValidated'); return { - $: 'OnRamp_MessageValidated', - fee: s.loadCoins(), - msg: loadCellRef(s, Router_CCIPSend.fromSlice), - context: Router_GetValidatedFeeContext.fromSlice(s), + $: 'TokenPool_Transfer', + id: s.loadUintBig(256), + details: loadCellRef>(s, + (s) => (() => { + return { + $: 'TokenPool_TransferDetails', + receiver: loadCellRef(s, CrossChainAddress.fromSlice), + remoteChainSelector: s.loadUintBig(64), + originalSender: s.loadAddress(), + amount: s.loadCoins(), + localToken: s.loadAddress(), + } + })() + ), } })(); }, - store(self: OnRamp_MessageValidated_GetValidatedFeeContext, b: c.Builder): void { - b.storeUint(0x2afb11bd, 32); - b.storeCoins(self.fee); - storeCellRef(self.msg, b, Router_CCIPSend.store); - Router_GetValidatedFeeContext.store(self.context, b); + store(self: TokenPool_LockOrBurnTransfer, b: c.Builder): void { + b.storeUint(self.id, 256); + storeCellRef>(self.details, b, + (v,b) => { storeCellRef(v.receiver, b, CrossChainAddress.store); + b.storeUint(v.remoteChainSelector, 64); + b.storeAddress(v.originalSender); + b.storeCoins(v.amount); + b.storeAddress(v.localToken); } + ); }, - toCell(self: OnRamp_MessageValidated_GetValidatedFeeContext): c.Cell { - return makeCellFrom(self, OnRamp_MessageValidated_GetValidatedFeeContext.store); + toCell(self: TokenPool_LockOrBurnTransfer): c.Cell { + return makeCellFrom(self, TokenPool_LockOrBurnTransfer.store); } } /** - > type OnRamp_MessageValidationFailed_GetValidatedFeeContext = OnRamp_MessageValidationFailed + > type TokenPool_ReleaseOrMintTransfer = TokenPool_Transfer, address, uint256> */ -export type OnRamp_MessageValidationFailed_GetValidatedFeeContext = OnRamp_MessageValidationFailed +export type TokenPool_ReleaseOrMintTransfer = TokenPool_Transfer -export const OnRamp_MessageValidationFailed_GetValidatedFeeContext = { - fromSlice(s: c.Slice): OnRamp_MessageValidationFailed_GetValidatedFeeContext { +export const TokenPool_ReleaseOrMintTransfer = { + fromSlice(s: c.Slice): TokenPool_ReleaseOrMintTransfer { return (() => { - loadAndCheckPrefix32(s, 0xac1dd12e, 'OnRamp_MessageValidationFailed'); return { - $: 'OnRamp_MessageValidationFailed', - error: s.loadUintBig(256), - msg: loadCellRef(s, Router_CCIPSend.fromSlice), - context: Router_GetValidatedFeeContext.fromSlice(s), + $: 'TokenPool_Transfer', + id: s.loadUintBig(256), + details: loadCellRef>(s, + (s) => (() => { + return { + $: 'TokenPool_TransferDetails', + receiver: s.loadAddress(), + remoteChainSelector: s.loadUintBig(64), + originalSender: loadCellRef(s, CrossChainAddress.fromSlice), + amount: s.loadUintBig(256), + localToken: s.loadAddress(), + } + })() + ), } })(); }, - store(self: OnRamp_MessageValidationFailed_GetValidatedFeeContext, b: c.Builder): void { - b.storeUint(0xac1dd12e, 32); - b.storeUint(self.error, 256); - storeCellRef(self.msg, b, Router_CCIPSend.store); - Router_GetValidatedFeeContext.store(self.context, b); + store(self: TokenPool_ReleaseOrMintTransfer, b: c.Builder): void { + b.storeUint(self.id, 256); + storeCellRef>(self.details, b, + (v,b) => { b.storeAddress(v.receiver); + b.storeUint(v.remoteChainSelector, 64); + storeCellRef(v.originalSender, b, CrossChainAddress.store); + b.storeUint(v.amount, 256); + b.storeAddress(v.localToken); } + ); }, - toCell(self: OnRamp_MessageValidationFailed_GetValidatedFeeContext): c.Cell { - return makeCellFrom(self, OnRamp_MessageValidationFailed_GetValidatedFeeContext.store); + toCell(self: TokenPool_ReleaseOrMintTransfer): c.Cell { + return makeCellFrom(self, TokenPool_ReleaseOrMintTransfer.store); } } /** - > struct (0x7db6745d) Router_ApplyRampUpdates { - > queryId: uint64 - > onRampUpdates: OnRamps? - > offRampAdds: OffRamps? - > offRampRemoves: OffRamps? + > struct TokenPool_LockOrBurnInV1 { + > transfer: TokenPool_LockOrBurnTransfer > } */ -export interface Router_ApplyRampUpdates { - readonly $: 'Router_ApplyRampUpdates' - queryId: uint64 - onRampUpdates: OnRamps | null /* = null */ - offRampAdds: OffRamps | null /* = null */ - offRampRemoves: OffRamps | null /* = null */ +export interface TokenPool_LockOrBurnInV1 { + readonly $: 'TokenPool_LockOrBurnInV1' + transfer: TokenPool_LockOrBurnTransfer } -export const Router_ApplyRampUpdates = { - PREFIX: 0x7db6745d, - +export const TokenPool_LockOrBurnInV1 = { create(args: { - queryId?: uint64 - onRampUpdates?: OnRamps | null /* = null */ - offRampAdds?: OffRamps | null /* = null */ - offRampRemoves?: OffRamps | null /* = null */ - }): Router_ApplyRampUpdates { + transfer: TokenPool_LockOrBurnTransfer + }): TokenPool_LockOrBurnInV1 { return { - $: 'Router_ApplyRampUpdates', - onRampUpdates: null, - offRampAdds: null, - offRampRemoves: null, - ...args, - queryId: args.queryId ?? 0n + $: 'TokenPool_LockOrBurnInV1', + ...args } }, - fromSlice(s: c.Slice): Router_ApplyRampUpdates { - loadAndCheckPrefix32(s, 0x7db6745d, 'Router_ApplyRampUpdates'); + fromSlice(s: c.Slice): TokenPool_LockOrBurnInV1 { return { - $: 'Router_ApplyRampUpdates', - queryId: s.loadUintBig(64), - onRampUpdates: s.loadBoolean() ? OnRamps.fromSlice(s) : null, - offRampAdds: s.loadBoolean() ? OffRamps.fromSlice(s) : null, - offRampRemoves: s.loadBoolean() ? OffRamps.fromSlice(s) : null, + $: 'TokenPool_LockOrBurnInV1', + transfer: TokenPool_LockOrBurnTransfer.fromSlice(s), } }, - store(self: Router_ApplyRampUpdates, b: c.Builder): void { - b.storeUint(0x7db6745d, 32); - b.storeUint(self.queryId, 64); - storeTolkNullable(self.onRampUpdates, b, OnRamps.store); - storeTolkNullable(self.offRampAdds, b, OffRamps.store); - storeTolkNullable(self.offRampRemoves, b, OffRamps.store); + store(self: TokenPool_LockOrBurnInV1, b: c.Builder): void { + TokenPool_LockOrBurnTransfer.store(self.transfer, b); }, - toCell(self: Router_ApplyRampUpdates): c.Cell { - return makeCellFrom(self, Router_ApplyRampUpdates.store); + toCell(self: TokenPool_LockOrBurnInV1): c.Cell { + return makeCellFrom(self, TokenPool_LockOrBurnInV1.store); } } /** - > struct (0x31768d95) Router_CCIPSend { - > queryID: uint64 - > destChainSelector: uint64 - > receiver: CrossChainAddress - > data: cell - > tokenAmounts: SnakedCell - > feeToken: address? - > extraArgs: Cell + > struct TokenPool_LockOrBurnOutV1 { + > destTokenAddress: Cell + > destPoolData: cell > } */ -export interface Router_CCIPSend { - readonly $: 'Router_CCIPSend' - queryID: uint64 - destChainSelector: uint64 - receiver: CrossChainAddress - data: c.Cell - tokenAmounts: SnakedCell - feeToken: c.Address | null - extraArgs: ExtraArgs +export interface TokenPool_LockOrBurnOutV1 { + readonly $: 'TokenPool_LockOrBurnOutV1' + destTokenAddress: CrossChainAddress + destPoolData: c.Cell } -export const Router_CCIPSend = { - PREFIX: 0x31768d95, - +export const TokenPool_LockOrBurnOutV1 = { create(args: { - queryID?: uint64 - destChainSelector: uint64 - receiver: CrossChainAddress - data: c.Cell - tokenAmounts: SnakedCell - feeToken: c.Address | null - extraArgs: ExtraArgs - }): Router_CCIPSend { + destTokenAddress: CrossChainAddress + destPoolData: c.Cell + }): TokenPool_LockOrBurnOutV1 { return { - $: 'Router_CCIPSend', - ...args, - queryID: args.queryID ?? 0n + $: 'TokenPool_LockOrBurnOutV1', + ...args } }, - fromSlice(s: c.Slice): Router_CCIPSend { - loadAndCheckPrefix32(s, 0x31768d95, 'Router_CCIPSend'); + fromSlice(s: c.Slice): TokenPool_LockOrBurnOutV1 { return { - $: 'Router_CCIPSend', - queryID: s.loadUintBig(64), - destChainSelector: s.loadUintBig(64), - receiver: CrossChainAddress.fromSlice(s), - data: s.loadRef(), - tokenAmounts: loadSnakedCellOf(s, TokenAmount.fromSlice), - feeToken: s.loadMaybeAddress(), - extraArgs: loadCellRef(s, ExtraArgs.fromSlice), + $: 'TokenPool_LockOrBurnOutV1', + destTokenAddress: loadCellRef(s, CrossChainAddress.fromSlice), + destPoolData: s.loadRef(), } }, - store(self: Router_CCIPSend, b: c.Builder): void { - b.storeUint(0x31768d95, 32); - b.storeUint(self.queryID, 64); - b.storeUint(self.destChainSelector, 64); - CrossChainAddress.store(self.receiver, b); - b.storeRef(self.data); - storeSnakedCellOf(self.tokenAmounts, b, TokenAmount.store); - b.storeAddress(self.feeToken); - storeCellRef(self.extraArgs, b, ExtraArgs.store); + store(self: TokenPool_LockOrBurnOutV1, b: c.Builder): void { + storeCellRef(self.destTokenAddress, b, CrossChainAddress.store); + b.storeRef(self.destPoolData); }, - toCell(self: Router_CCIPSend): c.Cell { - return makeCellFrom(self, Router_CCIPSend.store); + toCell(self: TokenPool_LockOrBurnOutV1): c.Cell { + return makeCellFrom(self, TokenPool_LockOrBurnOutV1.store); } } /** - > struct (0xfc69c50b) Router_RouteMessage { - > queryId: uint64 - > message: Cell - > execId: ReceiveExecutorId - > receiver: address - > gasLimit: coins + > struct TokenPool_ReleaseOrMintInV1 { + > transfer: TokenPool_ReleaseOrMintTransfer + > sourcePoolAddress: Cell + > sourcePoolData: cell? + > offchainTokenData: cell? > } */ -export interface Router_RouteMessage { - readonly $: 'Router_RouteMessage' - queryId: uint64 - message: Any2TVMMessage - execId: ReceiveExecutorId - receiver: c.Address - gasLimit: coins +export interface TokenPool_ReleaseOrMintInV1 { + readonly $: 'TokenPool_ReleaseOrMintInV1' + transfer: TokenPool_ReleaseOrMintTransfer + sourcePoolAddress: CrossChainAddress + sourcePoolData: c.Cell | null + offchainTokenData: c.Cell | null } -export const Router_RouteMessage = { - PREFIX: 0xfc69c50b, - +export const TokenPool_ReleaseOrMintInV1 = { create(args: { - queryId?: uint64 - message: Any2TVMMessage - execId: ReceiveExecutorId - receiver: c.Address - gasLimit: coins - }): Router_RouteMessage { + transfer: TokenPool_ReleaseOrMintTransfer + sourcePoolAddress: CrossChainAddress + sourcePoolData: c.Cell | null + offchainTokenData: c.Cell | null + }): TokenPool_ReleaseOrMintInV1 { return { - $: 'Router_RouteMessage', - ...args, - queryId: args.queryId ?? 0n + $: 'TokenPool_ReleaseOrMintInV1', + ...args } }, - fromSlice(s: c.Slice): Router_RouteMessage { - loadAndCheckPrefix32(s, 0xfc69c50b, 'Router_RouteMessage'); + fromSlice(s: c.Slice): TokenPool_ReleaseOrMintInV1 { return { - $: 'Router_RouteMessage', - queryId: s.loadUintBig(64), - message: loadCellRef(s, Any2TVMMessage.fromSlice), - execId: ReceiveExecutorId.fromSlice(s), - receiver: s.loadAddress(), - gasLimit: s.loadCoins(), + $: 'TokenPool_ReleaseOrMintInV1', + transfer: TokenPool_ReleaseOrMintTransfer.fromSlice(s), + sourcePoolAddress: loadCellRef(s, CrossChainAddress.fromSlice), + sourcePoolData: s.loadBoolean() ? s.loadRef() : null, + offchainTokenData: s.loadBoolean() ? s.loadRef() : null, } }, - store(self: Router_RouteMessage, b: c.Builder): void { - b.storeUint(0xfc69c50b, 32); - b.storeUint(self.queryId, 64); - storeCellRef(self.message, b, Any2TVMMessage.store); - ReceiveExecutorId.store(self.execId, b); - b.storeAddress(self.receiver); - b.storeCoins(self.gasLimit); + store(self: TokenPool_ReleaseOrMintInV1, b: c.Builder): void { + TokenPool_ReleaseOrMintTransfer.store(self.transfer, b); + storeCellRef(self.sourcePoolAddress, b, CrossChainAddress.store); + storeTolkNullable(self.sourcePoolData, b, + (v,b) => b.storeRef(v) + ); + storeTolkNullable(self.offchainTokenData, b, + (v,b) => b.storeRef(v) + ); }, - toCell(self: Router_RouteMessage): c.Cell { - return makeCellFrom(self, Router_RouteMessage.store); + toCell(self: TokenPool_ReleaseOrMintInV1): c.Cell { + return makeCellFrom(self, TokenPool_ReleaseOrMintInV1.store); } } /** - > struct (0x19f0098f) Router_WithdrawToTokenPool { - > queryID: uint64 - > destChainSelector: uint64 - > withdrawRequest: Cell + > struct OnRamps { + > destChainSelectors: SnakedCell + > onRamp: address? > } */ -export interface Router_WithdrawToTokenPool { - readonly $: 'Router_WithdrawToTokenPool' - queryID: uint64 - destChainSelector: uint64 - withdrawRequest: Router_WithdrawRequest +export interface OnRamps { + readonly $: 'OnRamps' + destChainSelectors: SnakedCell + onRamp: c.Address | null } -export const Router_WithdrawToTokenPool = { - PREFIX: 0x19f0098f, - +export const OnRamps = { create(args: { - queryID?: uint64 - destChainSelector: uint64 - withdrawRequest: Router_WithdrawRequest - }): Router_WithdrawToTokenPool { + destChainSelectors: SnakedCell + onRamp: c.Address | null + }): OnRamps { return { - $: 'Router_WithdrawToTokenPool', - ...args, - queryID: args.queryID ?? 0n + $: 'OnRamps', + ...args } }, - fromSlice(s: c.Slice): Router_WithdrawToTokenPool { - loadAndCheckPrefix32(s, 0x19f0098f, 'Router_WithdrawToTokenPool'); + fromSlice(s: c.Slice): OnRamps { return { - $: 'Router_WithdrawToTokenPool', - queryID: s.loadUintBig(64), - destChainSelector: s.loadUintBig(64), - withdrawRequest: loadCellRef(s, Router_WithdrawRequest.fromSlice), + $: 'OnRamps', + destChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), + onRamp: s.loadMaybeAddress(), } }, - store(self: Router_WithdrawToTokenPool, b: c.Builder): void { - b.storeUint(0x19f0098f, 32); - b.storeUint(self.queryID, 64); - b.storeUint(self.destChainSelector, 64); - storeCellRef(self.withdrawRequest, b, Router_WithdrawRequest.store); + store(self: OnRamps, b: c.Builder): void { + storeSnakedCellOf(self.destChainSelectors, b, (v, b) => b.storeUint(v, 64)); + b.storeAddress(self.onRamp); }, - toCell(self: Router_WithdrawToTokenPool): c.Cell { - return makeCellFrom(self, Router_WithdrawToTokenPool.store); + toCell(self: OnRamps): c.Cell { + return makeCellFrom(self, OnRamps.store); } } /** - > struct Router_WithdrawRequest { - > routerWalletAddress: address - > amount: coins - > tokenPool: address - > forwardPayload: Cell + > struct OffRamps { + > sourceChainSelectors: SnakedCell + > offRamp: address > } */ -export interface Router_WithdrawRequest { - readonly $: 'Router_WithdrawRequest' - routerWalletAddress: c.Address - amount: coins - tokenPool: c.Address - forwardPayload: TokenPool_LockOrBurnForwardPayload +export interface OffRamps { + readonly $: 'OffRamps' + sourceChainSelectors: SnakedCell + offRamp: c.Address } -export const Router_WithdrawRequest = { +export const OffRamps = { create(args: { - routerWalletAddress: c.Address - amount: coins - tokenPool: c.Address - forwardPayload: TokenPool_LockOrBurnForwardPayload - }): Router_WithdrawRequest { + sourceChainSelectors: SnakedCell + offRamp: c.Address + }): OffRamps { return { - $: 'Router_WithdrawRequest', + $: 'OffRamps', ...args } }, - fromSlice(s: c.Slice): Router_WithdrawRequest { + fromSlice(s: c.Slice): OffRamps { return { - $: 'Router_WithdrawRequest', - routerWalletAddress: s.loadAddress(), - amount: s.loadCoins(), - tokenPool: s.loadAddress(), - forwardPayload: loadCellRef(s, TokenPool_LockOrBurnForwardPayload.fromSlice), + $: 'OffRamps', + sourceChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), + offRamp: s.loadAddress(), } }, - store(self: Router_WithdrawRequest, b: c.Builder): void { - b.storeAddress(self.routerWalletAddress); - b.storeCoins(self.amount); - b.storeAddress(self.tokenPool); - storeCellRef(self.forwardPayload, b, TokenPool_LockOrBurnForwardPayload.store); + store(self: OffRamps, b: c.Builder): void { + storeSnakedCellOf(self.sourceChainSelectors, b, (v, b) => b.storeUint(v, 64)); + b.storeAddress(self.offRamp); }, - toCell(self: Router_WithdrawRequest): c.Cell { - return makeCellFrom(self, Router_WithdrawRequest.store); + toCell(self: OffRamps): c.Cell { + return makeCellFrom(self, OffRamps.store); } } /** - > struct (0x1e55bbf6) Router_CCIPReceiveConfirm { - > queryId: uint64 - > execId: ReceiveExecutorId - > } + > type Router_GetValidatedFee_Any = Router_GetValidatedFee */ -export interface Router_CCIPReceiveConfirm { - readonly $: 'Router_CCIPReceiveConfirm' - queryId: uint64 - execId: ReceiveExecutorId -} +export type Router_GetValidatedFee_Any = Router_GetValidatedFee -export const Router_CCIPReceiveConfirm = { - PREFIX: 0x1e55bbf6, +export const Router_GetValidatedFee_Any = { + fromSlice(s: c.Slice): Router_GetValidatedFee_Any { + return (() => { + loadAndCheckPrefix32(s, 0x4dd6aa82, 'Router_GetValidatedFee'); + return { + $: 'Router_GetValidatedFee', + ccipSend: loadCellRef(s, Router_CCIPSend.fromSlice), + context: loadTolkRemaining(s), + } + })(); + }, + store(self: Router_GetValidatedFee_Any, b: c.Builder): void { + b.storeUint(0x4dd6aa82, 32); + storeCellRef(self.ccipSend, b, Router_CCIPSend.store); + storeTolkRemaining(self.context, b); + }, + toCell(self: Router_GetValidatedFee_Any): c.Cell { + return makeCellFrom(self, Router_GetValidatedFee_Any.store); + } +} - create(args: { - queryId?: uint64 - execId: ReceiveExecutorId - }): Router_CCIPReceiveConfirm { - return { - $: 'Router_CCIPReceiveConfirm', - ...args, - queryId: args.queryId ?? 0n - } +/** + > type OnRamp_MessageValidated_GetValidatedFeeContext = OnRamp_MessageValidated + */ +export type OnRamp_MessageValidated_GetValidatedFeeContext = OnRamp_MessageValidated + +export const OnRamp_MessageValidated_GetValidatedFeeContext = { + fromSlice(s: c.Slice): OnRamp_MessageValidated_GetValidatedFeeContext { + return (() => { + loadAndCheckPrefix32(s, 0x2afb11bd, 'OnRamp_MessageValidated'); + return { + $: 'OnRamp_MessageValidated', + fee: s.loadCoins(), + msg: loadCellRef(s, Router_CCIPSend.fromSlice), + context: Router_GetValidatedFeeContext.fromSlice(s), + } + })(); }, - fromSlice(s: c.Slice): Router_CCIPReceiveConfirm { - loadAndCheckPrefix32(s, 0x1e55bbf6, 'Router_CCIPReceiveConfirm'); - return { - $: 'Router_CCIPReceiveConfirm', - queryId: s.loadUintBig(64), - execId: ReceiveExecutorId.fromSlice(s), - } + store(self: OnRamp_MessageValidated_GetValidatedFeeContext, b: c.Builder): void { + b.storeUint(0x2afb11bd, 32); + b.storeCoins(self.fee); + storeCellRef(self.msg, b, Router_CCIPSend.store); + Router_GetValidatedFeeContext.store(self.context, b); }, - store(self: Router_CCIPReceiveConfirm, b: c.Builder): void { - b.storeUint(0x1e55bbf6, 32); - b.storeUint(self.queryId, 64); - ReceiveExecutorId.store(self.execId, b); + toCell(self: OnRamp_MessageValidated_GetValidatedFeeContext): c.Cell { + return makeCellFrom(self, OnRamp_MessageValidated_GetValidatedFeeContext.store); + } +} + +/** + > type OnRamp_MessageValidationFailed_GetValidatedFeeContext = OnRamp_MessageValidationFailed + */ +export type OnRamp_MessageValidationFailed_GetValidatedFeeContext = OnRamp_MessageValidationFailed + +export const OnRamp_MessageValidationFailed_GetValidatedFeeContext = { + fromSlice(s: c.Slice): OnRamp_MessageValidationFailed_GetValidatedFeeContext { + return (() => { + loadAndCheckPrefix32(s, 0xac1dd12e, 'OnRamp_MessageValidationFailed'); + return { + $: 'OnRamp_MessageValidationFailed', + error: s.loadUintBig(256), + msg: loadCellRef(s, Router_CCIPSend.fromSlice), + context: Router_GetValidatedFeeContext.fromSlice(s), + } + })(); }, - toCell(self: Router_CCIPReceiveConfirm): c.Cell { - return makeCellFrom(self, Router_CCIPReceiveConfirm.store); + store(self: OnRamp_MessageValidationFailed_GetValidatedFeeContext, b: c.Builder): void { + b.storeUint(0xac1dd12e, 32); + b.storeUint(self.error, 256); + storeCellRef(self.msg, b, Router_CCIPSend.store); + Router_GetValidatedFeeContext.store(self.context, b); + }, + toCell(self: OnRamp_MessageValidationFailed_GetValidatedFeeContext): c.Cell { + return makeCellFrom(self, OnRamp_MessageValidationFailed_GetValidatedFeeContext.store); } } /** - > struct (0xf3388046) Router_RMNRemoteCurse { + > struct (0x7db6745d) Router_ApplyRampUpdates { > queryId: uint64 - > subjects: SnakedCell + > onRampUpdates: OnRamps? + > offRampAdds: OffRamps? + > offRampRemoves: OffRamps? > } */ -export interface Router_RMNRemoteCurse { - readonly $: 'Router_RMNRemoteCurse' +export interface Router_ApplyRampUpdates { + readonly $: 'Router_ApplyRampUpdates' queryId: uint64 - subjects: SnakedCell + onRampUpdates: OnRamps | null /* = null */ + offRampAdds: OffRamps | null /* = null */ + offRampRemoves: OffRamps | null /* = null */ } -export const Router_RMNRemoteCurse = { - PREFIX: 0xf3388046, +export const Router_ApplyRampUpdates = { + PREFIX: 0x7db6745d, create(args: { queryId?: uint64 - subjects: SnakedCell - }): Router_RMNRemoteCurse { + onRampUpdates?: OnRamps | null /* = null */ + offRampAdds?: OffRamps | null /* = null */ + offRampRemoves?: OffRamps | null /* = null */ + }): Router_ApplyRampUpdates { return { - $: 'Router_RMNRemoteCurse', + $: 'Router_ApplyRampUpdates', + onRampUpdates: null, + offRampAdds: null, + offRampRemoves: null, ...args, queryId: args.queryId ?? 0n } }, - fromSlice(s: c.Slice): Router_RMNRemoteCurse { - loadAndCheckPrefix32(s, 0xf3388046, 'Router_RMNRemoteCurse'); + fromSlice(s: c.Slice): Router_ApplyRampUpdates { + loadAndCheckPrefix32(s, 0x7db6745d, 'Router_ApplyRampUpdates'); return { - $: 'Router_RMNRemoteCurse', + $: 'Router_ApplyRampUpdates', queryId: s.loadUintBig(64), - subjects: loadSnakedCellOf(s, (s) => s.loadUintBig(128)), + onRampUpdates: s.loadBoolean() ? OnRamps.fromSlice(s) : null, + offRampAdds: s.loadBoolean() ? OffRamps.fromSlice(s) : null, + offRampRemoves: s.loadBoolean() ? OffRamps.fromSlice(s) : null, } }, - store(self: Router_RMNRemoteCurse, b: c.Builder): void { - b.storeUint(0xf3388046, 32); + store(self: Router_ApplyRampUpdates, b: c.Builder): void { + b.storeUint(0x7db6745d, 32); b.storeUint(self.queryId, 64); - storeSnakedCellOf(self.subjects, b, (v, b) => b.storeUint(v, 128)); + storeTolkNullable(self.onRampUpdates, b, OnRamps.store); + storeTolkNullable(self.offRampAdds, b, OffRamps.store); + storeTolkNullable(self.offRampRemoves, b, OffRamps.store); }, - toCell(self: Router_RMNRemoteCurse): c.Cell { - return makeCellFrom(self, Router_RMNRemoteCurse.store); + toCell(self: Router_ApplyRampUpdates): c.Cell { + return makeCellFrom(self, Router_ApplyRampUpdates.store); } } /** - > struct (0x3f153a31) Router_RMNRemoteUncurse { - > queryId: uint64 - > subjects: SnakedCell + > struct (0x31768d95) Router_CCIPSend { + > queryID: uint64 + > destChainSelector: uint64 + > receiver: CrossChainAddress + > data: cell + > tokenAmounts: SnakedCell + > feeToken: address? + > extraArgs: Cell > } */ -export interface Router_RMNRemoteUncurse { - readonly $: 'Router_RMNRemoteUncurse' - queryId: uint64 - subjects: SnakedCell +export interface Router_CCIPSend { + readonly $: 'Router_CCIPSend' + queryID: uint64 + destChainSelector: uint64 + receiver: CrossChainAddress + data: c.Cell + tokenAmounts: SnakedCell + feeToken: c.Address | null + extraArgs: ExtraArgs } -export const Router_RMNRemoteUncurse = { - PREFIX: 0x3f153a31, +export const Router_CCIPSend = { + PREFIX: 0x31768d95, create(args: { - queryId?: uint64 - subjects: SnakedCell - }): Router_RMNRemoteUncurse { + queryID?: uint64 + destChainSelector: uint64 + receiver: CrossChainAddress + data: c.Cell + tokenAmounts: SnakedCell + feeToken: c.Address | null + extraArgs: ExtraArgs + }): Router_CCIPSend { return { - $: 'Router_RMNRemoteUncurse', + $: 'Router_CCIPSend', ...args, - queryId: args.queryId ?? 0n + queryID: args.queryID ?? 0n } }, - fromSlice(s: c.Slice): Router_RMNRemoteUncurse { - loadAndCheckPrefix32(s, 0x3f153a31, 'Router_RMNRemoteUncurse'); + fromSlice(s: c.Slice): Router_CCIPSend { + loadAndCheckPrefix32(s, 0x31768d95, 'Router_CCIPSend'); return { - $: 'Router_RMNRemoteUncurse', - queryId: s.loadUintBig(64), - subjects: loadSnakedCellOf(s, (s) => s.loadUintBig(128)), + $: 'Router_CCIPSend', + queryID: s.loadUintBig(64), + destChainSelector: s.loadUintBig(64), + receiver: CrossChainAddress.fromSlice(s), + data: s.loadRef(), + tokenAmounts: loadSnakedCellOf(s, TokenAmount.fromSlice), + feeToken: s.loadMaybeAddress(), + extraArgs: loadCellRef(s, ExtraArgs.fromSlice), } }, - store(self: Router_RMNRemoteUncurse, b: c.Builder): void { - b.storeUint(0x3f153a31, 32); - b.storeUint(self.queryId, 64); - storeSnakedCellOf(self.subjects, b, (v, b) => b.storeUint(v, 128)); + store(self: Router_CCIPSend, b: c.Builder): void { + b.storeUint(0x31768d95, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.destChainSelector, 64); + CrossChainAddress.store(self.receiver, b); + b.storeRef(self.data); + storeSnakedCellOf(self.tokenAmounts, b, TokenAmount.store); + b.storeAddress(self.feeToken); + storeCellRef(self.extraArgs, b, ExtraArgs.store); }, - toCell(self: Router_RMNRemoteUncurse): c.Cell { - return makeCellFrom(self, Router_RMNRemoteUncurse.store); + toCell(self: Router_CCIPSend): c.Cell { + return makeCellFrom(self, Router_CCIPSend.store); } } /** - > struct (0x0b95aa4e) Router_RMNRemoteVerifyNotCursed { + > struct (0xfc69c50b) Router_RouteMessage { > queryId: uint64 - > subject: uint128 + > message: Cell + > execId: ReceiveExecutorId + > receiver: address + > gasLimit: coins > } */ -export interface Router_RMNRemoteVerifyNotCursed { - readonly $: 'Router_RMNRemoteVerifyNotCursed' +export interface Router_RouteMessage { + readonly $: 'Router_RouteMessage' queryId: uint64 - subject: uint128 + message: Any2TVMMessage + execId: ReceiveExecutorId + receiver: c.Address + gasLimit: coins } -export const Router_RMNRemoteVerifyNotCursed = { - PREFIX: 0x0b95aa4e, +export const Router_RouteMessage = { + PREFIX: 0xfc69c50b, create(args: { queryId?: uint64 - subject: uint128 - }): Router_RMNRemoteVerifyNotCursed { + message: Any2TVMMessage + execId: ReceiveExecutorId + receiver: c.Address + gasLimit: coins + }): Router_RouteMessage { return { - $: 'Router_RMNRemoteVerifyNotCursed', + $: 'Router_RouteMessage', ...args, queryId: args.queryId ?? 0n } }, - fromSlice(s: c.Slice): Router_RMNRemoteVerifyNotCursed { - loadAndCheckPrefix32(s, 0x0b95aa4e, 'Router_RMNRemoteVerifyNotCursed'); + fromSlice(s: c.Slice): Router_RouteMessage { + loadAndCheckPrefix32(s, 0xfc69c50b, 'Router_RouteMessage'); return { - $: 'Router_RMNRemoteVerifyNotCursed', + $: 'Router_RouteMessage', queryId: s.loadUintBig(64), - subject: s.loadUintBig(128), - } - }, - store(self: Router_RMNRemoteVerifyNotCursed, b: c.Builder): void { - b.storeUint(0x0b95aa4e, 32); - b.storeUint(self.queryId, 64); - b.storeUint(self.subject, 128); - }, - toCell(self: Router_RMNRemoteVerifyNotCursed): c.Cell { - return makeCellFrom(self, Router_RMNRemoteVerifyNotCursed.store); - } -} - -/** - > struct (0x22ba83b3) Router_RMNRemoteVerifyNotCursedResponse { - > queryId: uint64 - > result: bool - > } - */ -export interface Router_RMNRemoteVerifyNotCursedResponse { - readonly $: 'Router_RMNRemoteVerifyNotCursedResponse' - queryId: uint64 - result: boolean -} - -export const Router_RMNRemoteVerifyNotCursedResponse = { - PREFIX: 0x22ba83b3, - - create(args: { - queryId?: uint64 - result: boolean - }): Router_RMNRemoteVerifyNotCursedResponse { - return { - $: 'Router_RMNRemoteVerifyNotCursedResponse', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): Router_RMNRemoteVerifyNotCursedResponse { - loadAndCheckPrefix32(s, 0x22ba83b3, 'Router_RMNRemoteVerifyNotCursedResponse'); - return { - $: 'Router_RMNRemoteVerifyNotCursedResponse', - queryId: s.loadUintBig(64), - result: s.loadBoolean(), + message: loadCellRef(s, Any2TVMMessage.fromSlice), + execId: ReceiveExecutorId.fromSlice(s), + receiver: s.loadAddress(), + gasLimit: s.loadCoins(), } }, - store(self: Router_RMNRemoteVerifyNotCursedResponse, b: c.Builder): void { - b.storeUint(0x22ba83b3, 32); + store(self: Router_RouteMessage, b: c.Builder): void { + b.storeUint(0xfc69c50b, 32); b.storeUint(self.queryId, 64); - b.storeBit(self.result); + storeCellRef(self.message, b, Any2TVMMessage.store); + ReceiveExecutorId.store(self.execId, b); + b.storeAddress(self.receiver); + b.storeCoins(self.gasLimit); }, - toCell(self: Router_RMNRemoteVerifyNotCursedResponse): c.Cell { - return makeCellFrom(self, Router_RMNRemoteVerifyNotCursedResponse.store); + toCell(self: Router_RouteMessage): c.Cell { + return makeCellFrom(self, Router_RouteMessage.store); } } /** - > struct (0x6513f8e1) Router_MessageSent { + > struct (0x19f0098f) Router_WithdrawToTokenPool { > queryID: uint64 - > messageId: uint256 > destChainSelector: uint64 - > sender: address + > withdrawRequest: Cell > } */ -export interface Router_MessageSent { - readonly $: 'Router_MessageSent' +export interface Router_WithdrawToTokenPool { + readonly $: 'Router_WithdrawToTokenPool' queryID: uint64 - messageId: uint256 destChainSelector: uint64 - sender: c.Address + withdrawRequest: Router_WithdrawRequest } -export const Router_MessageSent = { - PREFIX: 0x6513f8e1, +export const Router_WithdrawToTokenPool = { + PREFIX: 0x19f0098f, create(args: { queryID?: uint64 - messageId: uint256 destChainSelector: uint64 - sender: c.Address - }): Router_MessageSent { + withdrawRequest: Router_WithdrawRequest + }): Router_WithdrawToTokenPool { return { - $: 'Router_MessageSent', + $: 'Router_WithdrawToTokenPool', ...args, queryID: args.queryID ?? 0n } }, - fromSlice(s: c.Slice): Router_MessageSent { - loadAndCheckPrefix32(s, 0x6513f8e1, 'Router_MessageSent'); + fromSlice(s: c.Slice): Router_WithdrawToTokenPool { + loadAndCheckPrefix32(s, 0x19f0098f, 'Router_WithdrawToTokenPool'); return { - $: 'Router_MessageSent', + $: 'Router_WithdrawToTokenPool', queryID: s.loadUintBig(64), - messageId: s.loadUintBig(256), destChainSelector: s.loadUintBig(64), - sender: s.loadAddress(), + withdrawRequest: loadCellRef(s, Router_WithdrawRequest.fromSlice), } }, - store(self: Router_MessageSent, b: c.Builder): void { - b.storeUint(0x6513f8e1, 32); + store(self: Router_WithdrawToTokenPool, b: c.Builder): void { + b.storeUint(0x19f0098f, 32); b.storeUint(self.queryID, 64); - b.storeUint(self.messageId, 256); b.storeUint(self.destChainSelector, 64); - b.storeAddress(self.sender); + storeCellRef(self.withdrawRequest, b, Router_WithdrawRequest.store); }, - toCell(self: Router_MessageSent): c.Cell { - return makeCellFrom(self, Router_MessageSent.store); + toCell(self: Router_WithdrawToTokenPool): c.Cell { + return makeCellFrom(self, Router_WithdrawToTokenPool.store); } } /** - > struct (0x8ae25114) Router_MessageRejected { - > queryID: uint64 - > destChainSelector: uint64 - > sender: address - > error: uint256 + > struct Router_WithdrawRequest { + > routerWalletAddress: address + > amount: coins + > tokenPool: address + > forwardPayload: Cell > } */ -export interface Router_MessageRejected { - readonly $: 'Router_MessageRejected' - queryID: uint64 - destChainSelector: uint64 - sender: c.Address - error: uint256 +export interface Router_WithdrawRequest { + readonly $: 'Router_WithdrawRequest' + routerWalletAddress: c.Address + amount: coins + tokenPool: c.Address + forwardPayload: TokenPool_LockOrBurnForwardPayload } -export const Router_MessageRejected = { - PREFIX: 0x8ae25114, - +export const Router_WithdrawRequest = { create(args: { - queryID?: uint64 - destChainSelector: uint64 - sender: c.Address - error: uint256 - }): Router_MessageRejected { + routerWalletAddress: c.Address + amount: coins + tokenPool: c.Address + forwardPayload: TokenPool_LockOrBurnForwardPayload + }): Router_WithdrawRequest { return { - $: 'Router_MessageRejected', - ...args, - queryID: args.queryID ?? 0n + $: 'Router_WithdrawRequest', + ...args } }, - fromSlice(s: c.Slice): Router_MessageRejected { - loadAndCheckPrefix32(s, 0x8ae25114, 'Router_MessageRejected'); + fromSlice(s: c.Slice): Router_WithdrawRequest { return { - $: 'Router_MessageRejected', - queryID: s.loadUintBig(64), - destChainSelector: s.loadUintBig(64), - sender: s.loadAddress(), - error: s.loadUintBig(256), + $: 'Router_WithdrawRequest', + routerWalletAddress: s.loadAddress(), + amount: s.loadCoins(), + tokenPool: s.loadAddress(), + forwardPayload: loadCellRef(s, TokenPool_LockOrBurnForwardPayload.fromSlice), } }, - store(self: Router_MessageRejected, b: c.Builder): void { - b.storeUint(0x8ae25114, 32); - b.storeUint(self.queryID, 64); - b.storeUint(self.destChainSelector, 64); - b.storeAddress(self.sender); - b.storeUint(self.error, 256); + store(self: Router_WithdrawRequest, b: c.Builder): void { + b.storeAddress(self.routerWalletAddress); + b.storeCoins(self.amount); + b.storeAddress(self.tokenPool); + storeCellRef(self.forwardPayload, b, TokenPool_LockOrBurnForwardPayload.store); }, - toCell(self: Router_MessageRejected): c.Cell { - return makeCellFrom(self, Router_MessageRejected.store); + toCell(self: Router_WithdrawRequest): c.Cell { + return makeCellFrom(self, Router_WithdrawRequest.store); } } /** - > struct (0xaf7a9ac6) Router_RMNOwnableMessage { - > content: RemainingBitsAndRefs + > struct (0x1e55bbf6) Router_CCIPReceiveConfirm { + > queryId: uint64 + > execId: ReceiveExecutorId > } */ -export interface Router_RMNOwnableMessage { - readonly $: 'Router_RMNOwnableMessage' - content: RemainingBitsAndRefs +export interface Router_CCIPReceiveConfirm { + readonly $: 'Router_CCIPReceiveConfirm' + queryId: uint64 + execId: ReceiveExecutorId } -export const Router_RMNOwnableMessage = { - PREFIX: 0xaf7a9ac6, +export const Router_CCIPReceiveConfirm = { + PREFIX: 0x1e55bbf6, create(args: { - content: RemainingBitsAndRefs - }): Router_RMNOwnableMessage { + queryId?: uint64 + execId: ReceiveExecutorId + }): Router_CCIPReceiveConfirm { return { - $: 'Router_RMNOwnableMessage', - ...args + $: 'Router_CCIPReceiveConfirm', + ...args, + queryId: args.queryId ?? 0n } }, - fromSlice(s: c.Slice): Router_RMNOwnableMessage { - loadAndCheckPrefix32(s, 0xaf7a9ac6, 'Router_RMNOwnableMessage'); + fromSlice(s: c.Slice): Router_CCIPReceiveConfirm { + loadAndCheckPrefix32(s, 0x1e55bbf6, 'Router_CCIPReceiveConfirm'); return { - $: 'Router_RMNOwnableMessage', - content: loadTolkRemaining(s), + $: 'Router_CCIPReceiveConfirm', + queryId: s.loadUintBig(64), + execId: ReceiveExecutorId.fromSlice(s), } }, - store(self: Router_RMNOwnableMessage, b: c.Builder): void { - b.storeUint(0xaf7a9ac6, 32); - storeTolkRemaining(self.content, b); + store(self: Router_CCIPReceiveConfirm, b: c.Builder): void { + b.storeUint(0x1e55bbf6, 32); + b.storeUint(self.queryId, 64); + ReceiveExecutorId.store(self.execId, b); }, - toCell(self: Router_RMNOwnableMessage): c.Cell { - return makeCellFrom(self, Router_RMNOwnableMessage.store); + toCell(self: Router_CCIPReceiveConfirm): c.Cell { + return makeCellFrom(self, Router_CCIPReceiveConfirm.store); } } /** - > struct (0x78d0f21e) Router_CCIPSendACK { - > queryID: uint64 - > messageId: uint256 + > struct (0xf3388046) Router_RMNRemoteCurse { + > queryId: uint64 + > subjects: SnakedCell > } */ -export interface Router_CCIPSendACK { - readonly $: 'Router_CCIPSendACK' - queryID: uint64 - messageId: uint256 +export interface Router_RMNRemoteCurse { + readonly $: 'Router_RMNRemoteCurse' + queryId: uint64 + subjects: SnakedCell } -export const Router_CCIPSendACK = { - PREFIX: 0x78d0f21e, +export const Router_RMNRemoteCurse = { + PREFIX: 0xf3388046, create(args: { - queryID?: uint64 - messageId: uint256 - }): Router_CCIPSendACK { + queryId?: uint64 + subjects: SnakedCell + }): Router_RMNRemoteCurse { return { - $: 'Router_CCIPSendACK', + $: 'Router_RMNRemoteCurse', ...args, - queryID: args.queryID ?? 0n + queryId: args.queryId ?? 0n } }, - fromSlice(s: c.Slice): Router_CCIPSendACK { - loadAndCheckPrefix32(s, 0x78d0f21e, 'Router_CCIPSendACK'); + fromSlice(s: c.Slice): Router_RMNRemoteCurse { + loadAndCheckPrefix32(s, 0xf3388046, 'Router_RMNRemoteCurse'); return { - $: 'Router_CCIPSendACK', - queryID: s.loadUintBig(64), - messageId: s.loadUintBig(256), + $: 'Router_RMNRemoteCurse', + queryId: s.loadUintBig(64), + subjects: loadSnakedCellOf(s, (s) => s.loadUintBig(128)), } }, - store(self: Router_CCIPSendACK, b: c.Builder): void { - b.storeUint(0x78d0f21e, 32); - b.storeUint(self.queryID, 64); - b.storeUint(self.messageId, 256); + store(self: Router_RMNRemoteCurse, b: c.Builder): void { + b.storeUint(0xf3388046, 32); + b.storeUint(self.queryId, 64); + storeSnakedCellOf(self.subjects, b, (v, b) => b.storeUint(v, 128)); }, - toCell(self: Router_CCIPSendACK): c.Cell { - return makeCellFrom(self, Router_CCIPSendACK.store); + toCell(self: Router_RMNRemoteCurse): c.Cell { + return makeCellFrom(self, Router_RMNRemoteCurse.store); } } /** - > struct (0x5a45d434) Router_CCIPSendNACK { - > queryID: uint64 - > error: uint256 + > struct (0x3f153a31) Router_RMNRemoteUncurse { + > queryId: uint64 + > subjects: SnakedCell > } */ -export interface Router_CCIPSendNACK { - readonly $: 'Router_CCIPSendNACK' - queryID: uint64 - error: uint256 +export interface Router_RMNRemoteUncurse { + readonly $: 'Router_RMNRemoteUncurse' + queryId: uint64 + subjects: SnakedCell } -export const Router_CCIPSendNACK = { - PREFIX: 0x5a45d434, +export const Router_RMNRemoteUncurse = { + PREFIX: 0x3f153a31, create(args: { - queryID?: uint64 - error: uint256 - }): Router_CCIPSendNACK { + queryId?: uint64 + subjects: SnakedCell + }): Router_RMNRemoteUncurse { return { - $: 'Router_CCIPSendNACK', + $: 'Router_RMNRemoteUncurse', ...args, - queryID: args.queryID ?? 0n + queryId: args.queryId ?? 0n } }, - fromSlice(s: c.Slice): Router_CCIPSendNACK { - loadAndCheckPrefix32(s, 0x5a45d434, 'Router_CCIPSendNACK'); + fromSlice(s: c.Slice): Router_RMNRemoteUncurse { + loadAndCheckPrefix32(s, 0x3f153a31, 'Router_RMNRemoteUncurse'); return { - $: 'Router_CCIPSendNACK', - queryID: s.loadUintBig(64), - error: s.loadUintBig(256), + $: 'Router_RMNRemoteUncurse', + queryId: s.loadUintBig(64), + subjects: loadSnakedCellOf(s, (s) => s.loadUintBig(128)), } }, - store(self: Router_CCIPSendNACK, b: c.Builder): void { - b.storeUint(0x5a45d434, 32); - b.storeUint(self.queryID, 64); - b.storeUint(self.error, 256); + store(self: Router_RMNRemoteUncurse, b: c.Builder): void { + b.storeUint(0x3f153a31, 32); + b.storeUint(self.queryId, 64); + storeSnakedCellOf(self.subjects, b, (v, b) => b.storeUint(v, 128)); }, - toCell(self: Router_CCIPSendNACK): c.Cell { - return makeCellFrom(self, Router_CCIPSendNACK.store); + toCell(self: Router_RMNRemoteUncurse): c.Cell { + return makeCellFrom(self, Router_RMNRemoteUncurse.store); } } /** - > struct (0x4dd6aa82) Router_GetValidatedFee { - > ccipSend: Cell - > context: T + > struct (0x0b95aa4e) Router_RMNRemoteVerifyNotCursed { + > queryId: uint64 + > subject: uint128 > } */ -export interface Router_GetValidatedFee { - readonly $: 'Router_GetValidatedFee' - ccipSend: Router_CCIPSend - context: T +export interface Router_RMNRemoteVerifyNotCursed { + readonly $: 'Router_RMNRemoteVerifyNotCursed' + queryId: uint64 + subject: uint128 } -export const Router_GetValidatedFee = { - PREFIX: 0x4dd6aa82, +export const Router_RMNRemoteVerifyNotCursed = { + PREFIX: 0x0b95aa4e, - create(args: { - ccipSend: Router_CCIPSend - context: T - }): Router_GetValidatedFee { + create(args: { + queryId?: uint64 + subject: uint128 + }): Router_RMNRemoteVerifyNotCursed { return { - $: 'Router_GetValidatedFee', - ...args + $: 'Router_RMNRemoteVerifyNotCursed', + ...args, + queryId: args.queryId ?? 0n + } + }, + fromSlice(s: c.Slice): Router_RMNRemoteVerifyNotCursed { + loadAndCheckPrefix32(s, 0x0b95aa4e, 'Router_RMNRemoteVerifyNotCursed'); + return { + $: 'Router_RMNRemoteVerifyNotCursed', + queryId: s.loadUintBig(64), + subject: s.loadUintBig(128), } }, + store(self: Router_RMNRemoteVerifyNotCursed, b: c.Builder): void { + b.storeUint(0x0b95aa4e, 32); + b.storeUint(self.queryId, 64); + b.storeUint(self.subject, 128); + }, + toCell(self: Router_RMNRemoteVerifyNotCursed): c.Cell { + return makeCellFrom(self, Router_RMNRemoteVerifyNotCursed.store); + } } /** - > struct (0x6f2d00df) Router_LockOrBurn { - > queryID: uint64 - > tokenPool: address - > request: Cell - > executorAddress: address + > struct (0x22ba83b3) Router_RMNRemoteVerifyNotCursedResponse { + > queryId: uint64 + > result: bool > } */ -export interface Router_LockOrBurn { - readonly $: 'Router_LockOrBurn' - queryID: uint64 - tokenPool: c.Address - request: TokenPool_LockOrBurnInV1 - executorAddress: c.Address +export interface Router_RMNRemoteVerifyNotCursedResponse { + readonly $: 'Router_RMNRemoteVerifyNotCursedResponse' + queryId: uint64 + result: boolean } -export const Router_LockOrBurn = { - PREFIX: 0x6f2d00df, +export const Router_RMNRemoteVerifyNotCursedResponse = { + PREFIX: 0x22ba83b3, create(args: { - queryID?: uint64 - tokenPool: c.Address - request: TokenPool_LockOrBurnInV1 - executorAddress: c.Address - }): Router_LockOrBurn { + queryId?: uint64 + result: boolean + }): Router_RMNRemoteVerifyNotCursedResponse { return { - $: 'Router_LockOrBurn', + $: 'Router_RMNRemoteVerifyNotCursedResponse', ...args, - queryID: args.queryID ?? 0n + queryId: args.queryId ?? 0n } }, - fromSlice(s: c.Slice): Router_LockOrBurn { - loadAndCheckPrefix32(s, 0x6f2d00df, 'Router_LockOrBurn'); + fromSlice(s: c.Slice): Router_RMNRemoteVerifyNotCursedResponse { + loadAndCheckPrefix32(s, 0x22ba83b3, 'Router_RMNRemoteVerifyNotCursedResponse'); return { - $: 'Router_LockOrBurn', - queryID: s.loadUintBig(64), - tokenPool: s.loadAddress(), - request: loadCellRef(s, TokenPool_LockOrBurnInV1.fromSlice), - executorAddress: s.loadAddress(), + $: 'Router_RMNRemoteVerifyNotCursedResponse', + queryId: s.loadUintBig(64), + result: s.loadBoolean(), } }, - store(self: Router_LockOrBurn, b: c.Builder): void { - b.storeUint(0x6f2d00df, 32); - b.storeUint(self.queryID, 64); - b.storeAddress(self.tokenPool); - storeCellRef(self.request, b, TokenPool_LockOrBurnInV1.store); - b.storeAddress(self.executorAddress); + store(self: Router_RMNRemoteVerifyNotCursedResponse, b: c.Builder): void { + b.storeUint(0x22ba83b3, 32); + b.storeUint(self.queryId, 64); + b.storeBit(self.result); }, - toCell(self: Router_LockOrBurn): c.Cell { - return makeCellFrom(self, Router_LockOrBurn.store); + toCell(self: Router_RMNRemoteVerifyNotCursedResponse): c.Cell { + return makeCellFrom(self, Router_RMNRemoteVerifyNotCursedResponse.store); } } /** - > struct Router_GetValidatedFeeContext { - > routerContext: address - > userContext: RemainingBitsOrRef + > struct (0x6513f8e1) Router_MessageSent { + > queryID: uint64 + > messageId: uint256 + > destChainSelector: uint64 + > sender: address > } */ -export interface Router_GetValidatedFeeContext { - readonly $: 'Router_GetValidatedFeeContext' - routerContext: c.Address - userContext: RemainingBitsOrRef +export interface Router_MessageSent { + readonly $: 'Router_MessageSent' + queryID: uint64 + messageId: uint256 + destChainSelector: uint64 + sender: c.Address } -export const Router_GetValidatedFeeContext = { +export const Router_MessageSent = { + PREFIX: 0x6513f8e1, + create(args: { - routerContext: c.Address - userContext: RemainingBitsOrRef - }): Router_GetValidatedFeeContext { + queryID?: uint64 + messageId: uint256 + destChainSelector: uint64 + sender: c.Address + }): Router_MessageSent { return { - $: 'Router_GetValidatedFeeContext', - ...args + $: 'Router_MessageSent', + ...args, + queryID: args.queryID ?? 0n } }, - fromSlice(s: c.Slice): Router_GetValidatedFeeContext { + fromSlice(s: c.Slice): Router_MessageSent { + loadAndCheckPrefix32(s, 0x6513f8e1, 'Router_MessageSent'); return { - $: 'Router_GetValidatedFeeContext', - routerContext: s.loadAddress(), - userContext: loadTolkRemaining(s), + $: 'Router_MessageSent', + queryID: s.loadUintBig(64), + messageId: s.loadUintBig(256), + destChainSelector: s.loadUintBig(64), + sender: s.loadAddress(), } }, - store(self: Router_GetValidatedFeeContext, b: c.Builder): void { - b.storeAddress(self.routerContext); - storeTolkRemaining(self.userContext, b); + store(self: Router_MessageSent, b: c.Builder): void { + b.storeUint(0x6513f8e1, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.messageId, 256); + b.storeUint(self.destChainSelector, 64); + b.storeAddress(self.sender); }, - toCell(self: Router_GetValidatedFeeContext): c.Cell { - return makeCellFrom(self, Router_GetValidatedFeeContext.store); + toCell(self: Router_MessageSent): c.Cell { + return makeCellFrom(self, Router_MessageSent.store); } } /** - > type Router_MessageValidated_Any = Router_MessageValidated + > struct (0x8ae25114) Router_MessageRejected { + > queryID: uint64 + > destChainSelector: uint64 + > sender: address + > error: uint256 + > } */ -export type Router_MessageValidated_Any = Router_MessageValidated +export interface Router_MessageRejected { + readonly $: 'Router_MessageRejected' + queryID: uint64 + destChainSelector: uint64 + sender: c.Address + error: uint256 +} -export const Router_MessageValidated_Any = { - fromSlice(s: c.Slice): Router_MessageValidated_Any { - return (() => { - loadAndCheckPrefix32(s, 0x9e2155ec, 'Router_MessageValidated'); - return { - $: 'Router_MessageValidated', - fee: s.loadCoins(), - msg: loadCellRef(s, Router_CCIPSend.fromSlice), - context: loadTolkRemaining(s), - } - })(); - }, - store(self: Router_MessageValidated_Any, b: c.Builder): void { - b.storeUint(0x9e2155ec, 32); - b.storeCoins(self.fee); - storeCellRef(self.msg, b, Router_CCIPSend.store); - storeTolkRemaining(self.context, b); - }, - toCell(self: Router_MessageValidated_Any): c.Cell { - return makeCellFrom(self, Router_MessageValidated_Any.store); - } -} - -/** - > struct (0x9e2155ec) Router_MessageValidated { - > fee: coins - > msg: Cell - > context: RemainingBitsOrRef - > } - */ -export interface Router_MessageValidated { - readonly $: 'Router_MessageValidated' - fee: coins - msg: Router_CCIPSend - context: RemainingBitsOrRef -} - -export const Router_MessageValidated = { - PREFIX: 0x9e2155ec, +export const Router_MessageRejected = { + PREFIX: 0x8ae25114, - create(args: { - fee: coins - msg: Router_CCIPSend - context: RemainingBitsOrRef - }): Router_MessageValidated { + create(args: { + queryID?: uint64 + destChainSelector: uint64 + sender: c.Address + error: uint256 + }): Router_MessageRejected { return { - $: 'Router_MessageValidated', - ...args + $: 'Router_MessageRejected', + ...args, + queryID: args.queryID ?? 0n } }, -} - -/** - > type Router_MessageValidationFailed_Any = Router_MessageValidationFailed - */ -export type Router_MessageValidationFailed_Any = Router_MessageValidationFailed - -export const Router_MessageValidationFailed_Any = { - fromSlice(s: c.Slice): Router_MessageValidationFailed_Any { - return (() => { - loadAndCheckPrefix32(s, 0xec23c562, 'Router_MessageValidationFailed'); - return { - $: 'Router_MessageValidationFailed', - error: s.loadUintBig(256), - msg: loadCellRef(s, Router_CCIPSend.fromSlice), - context: loadTolkRemaining(s), - } - })(); + fromSlice(s: c.Slice): Router_MessageRejected { + loadAndCheckPrefix32(s, 0x8ae25114, 'Router_MessageRejected'); + return { + $: 'Router_MessageRejected', + queryID: s.loadUintBig(64), + destChainSelector: s.loadUintBig(64), + sender: s.loadAddress(), + error: s.loadUintBig(256), + } }, - store(self: Router_MessageValidationFailed_Any, b: c.Builder): void { - b.storeUint(0xec23c562, 32); + store(self: Router_MessageRejected, b: c.Builder): void { + b.storeUint(0x8ae25114, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.destChainSelector, 64); + b.storeAddress(self.sender); b.storeUint(self.error, 256); - storeCellRef(self.msg, b, Router_CCIPSend.store); - storeTolkRemaining(self.context, b); }, - toCell(self: Router_MessageValidationFailed_Any): c.Cell { - return makeCellFrom(self, Router_MessageValidationFailed_Any.store); + toCell(self: Router_MessageRejected): c.Cell { + return makeCellFrom(self, Router_MessageRejected.store); } } /** - > struct (0xec23c562) Router_MessageValidationFailed { - > error: uint256 - > msg: Cell - > context: RemainingBitsOrRef + > struct (0xaf7a9ac6) Router_RMNOwnableMessage { + > content: RemainingBitsAndRefs > } */ -export interface Router_MessageValidationFailed { - readonly $: 'Router_MessageValidationFailed' - error: uint256 - msg: Router_CCIPSend - context: RemainingBitsOrRef +export interface Router_RMNOwnableMessage { + readonly $: 'Router_RMNOwnableMessage' + content: RemainingBitsAndRefs } -export const Router_MessageValidationFailed = { - PREFIX: 0xec23c562, +export const Router_RMNOwnableMessage = { + PREFIX: 0xaf7a9ac6, - create(args: { - error: uint256 - msg: Router_CCIPSend - context: RemainingBitsOrRef - }): Router_MessageValidationFailed { + create(args: { + content: RemainingBitsAndRefs + }): Router_RMNOwnableMessage { return { - $: 'Router_MessageValidationFailed', + $: 'Router_RMNOwnableMessage', ...args } }, + fromSlice(s: c.Slice): Router_RMNOwnableMessage { + loadAndCheckPrefix32(s, 0xaf7a9ac6, 'Router_RMNOwnableMessage'); + return { + $: 'Router_RMNOwnableMessage', + content: loadTolkRemaining(s), + } + }, + store(self: Router_RMNOwnableMessage, b: c.Builder): void { + b.storeUint(0xaf7a9ac6, 32); + storeTolkRemaining(self.content, b); + }, + toCell(self: Router_RMNOwnableMessage): c.Cell { + return makeCellFrom(self, Router_RMNOwnableMessage.store); + } } /** - > struct RMNRemote { - > admin: Ownable2Step - > cursedSubjects: CursedSubjects - > forwardUpdates: map + > struct (0x78d0f21e) Router_CCIPSendACK { + > queryID: uint64 + > messageId: uint256 > } */ -export interface RMNRemote { - readonly $: 'RMNRemote' - admin: Ownable2Step - cursedSubjects: CursedSubjects - forwardUpdates: Set /* = [] as map */ +export interface Router_CCIPSendACK { + readonly $: 'Router_CCIPSendACK' + queryID: uint64 + messageId: uint256 } -export const RMNRemote = { +export const Router_CCIPSendACK = { + PREFIX: 0x78d0f21e, + create(args: { - admin: Ownable2Step - cursedSubjects: CursedSubjects - forwardUpdates: Set /* = [] as map */ - }): RMNRemote { + queryID?: uint64 + messageId: uint256 + }): Router_CCIPSendACK { return { - $: 'RMNRemote', - ...args + $: 'Router_CCIPSendACK', + ...args, + queryID: args.queryID ?? 0n } }, - fromSlice(s: c.Slice): RMNRemote { + fromSlice(s: c.Slice): Router_CCIPSendACK { + loadAndCheckPrefix32(s, 0x78d0f21e, 'Router_CCIPSendACK'); return { - $: 'RMNRemote', - admin: Ownable2Step.fromSlice(s), - cursedSubjects: CursedSubjects.fromSlice(s), - forwardUpdates: dictToSet(c.Dictionary.load(c.Dictionary.Keys.Address(), createDictionaryValue<[]>( - (s) => [], - (v,b) => { {} } - ), s)), + $: 'Router_CCIPSendACK', + queryID: s.loadUintBig(64), + messageId: s.loadUintBig(256), } }, - store(self: RMNRemote, b: c.Builder): void { - Ownable2Step.store(self.admin, b); - CursedSubjects.store(self.cursedSubjects, b); - b.storeDict(setToDict(self.forwardUpdates, c.Dictionary.Keys.Address(), createDictionaryValue<[]>( - (s) => [], - (v,b) => { {} } - )), c.Dictionary.Keys.Address(), createDictionaryValue<[]>( - (s) => [], - (v,b) => { {} } - )); + store(self: Router_CCIPSendACK, b: c.Builder): void { + b.storeUint(0x78d0f21e, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.messageId, 256); }, - toCell(self: RMNRemote): c.Cell { - return makeCellFrom(self, RMNRemote.store); + toCell(self: Router_CCIPSendACK): c.Cell { + return makeCellFrom(self, Router_CCIPSendACK.store); } } /** - > struct Storage { - > id: uint32 - > ownable: Ownable2Step - > wrappedNative: address - > onRamps: map - > offRamps: map - > rmnRemote: Cell + > struct (0x5a45d434) Router_CCIPSendNACK { + > queryID: uint64 + > error: uint256 > } */ -export interface Storage { - readonly $: 'Storage' - id: uint32 - ownable: Ownable2Step - wrappedNative: c.Address - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ - rmnRemote: RMNRemote +export interface Router_CCIPSendNACK { + readonly $: 'Router_CCIPSendNACK' + queryID: uint64 + error: uint256 } -export const Storage = { +export const Router_CCIPSendNACK = { + PREFIX: 0x5a45d434, + create(args: { - id: uint32 - ownable: Ownable2Step - wrappedNative: c.Address - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ - rmnRemote: RMNRemote - }): Storage { + queryID?: uint64 + error: uint256 + }): Router_CCIPSendNACK { return { - $: 'Storage', - ...args + $: 'Router_CCIPSendNACK', + ...args, + queryID: args.queryID ?? 0n } }, - fromSlice(s: c.Slice): Storage { + fromSlice(s: c.Slice): Router_CCIPSendNACK { + loadAndCheckPrefix32(s, 0x5a45d434, 'Router_CCIPSendNACK'); return { - $: 'Storage', - id: s.loadUintBig(32), - ownable: Ownable2Step.fromSlice(s), - wrappedNative: s.loadAddress(), - onRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), - offRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), - rmnRemote: loadCellRef(s, RMNRemote.fromSlice), + $: 'Router_CCIPSendNACK', + queryID: s.loadUintBig(64), + error: s.loadUintBig(256), } }, - store(self: Storage, b: c.Builder): void { - b.storeUint(self.id, 32); - Ownable2Step.store(self.ownable, b); - b.storeAddress(self.wrappedNative); - b.storeDict(mapToDict(self.onRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); - b.storeDict(mapToDict(self.offRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); - storeCellRef(self.rmnRemote, b, RMNRemote.store); + store(self: Router_CCIPSendNACK, b: c.Builder): void { + b.storeUint(0x5a45d434, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.error, 256); }, - toCell(self: Storage): c.Cell { - return makeCellFrom(self, Storage.store); + toCell(self: Router_CCIPSendNACK): c.Cell { + return makeCellFrom(self, Router_CCIPSendNACK.store); } } /** - > struct OnRampSet { - > destChainSelectors: SnakedCell - > onRamp: address? + > struct (0x4dd6aa82) Router_GetValidatedFee { + > ccipSend: Cell + > context: T > } */ -export interface OnRampSet { - readonly $: 'OnRampSet' - destChainSelectors: SnakedCell - onRamp: c.Address | null +export interface Router_GetValidatedFee { + readonly $: 'Router_GetValidatedFee' + ccipSend: Router_CCIPSend + context: T } -export const OnRampSet = { - create(args: { - destChainSelectors: SnakedCell - onRamp: c.Address | null - }): OnRampSet { +export const Router_GetValidatedFee = { + PREFIX: 0x4dd6aa82, + + create(args: { + ccipSend: Router_CCIPSend + context: T + }): Router_GetValidatedFee { return { - $: 'OnRampSet', + $: 'Router_GetValidatedFee', ...args } }, - fromSlice(s: c.Slice): OnRampSet { - return { - $: 'OnRampSet', - destChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), - onRamp: s.loadMaybeAddress(), - } - }, - store(self: OnRampSet, b: c.Builder): void { - storeSnakedCellOf(self.destChainSelectors, b, (v, b) => b.storeUint(v, 64)); - b.storeAddress(self.onRamp); - }, - toCell(self: OnRampSet): c.Cell { - return makeCellFrom(self, OnRampSet.store); - } } /** - > struct OffRampAdded { - > sourceChainSelectors: SnakedCell - > offRampAdded: address + > struct (0x6f2d00df) Router_LockOrBurn { + > queryID: uint64 + > tokenPool: address + > request: Cell + > executorAddress: address > } */ -export interface OffRampAdded { - readonly $: 'OffRampAdded' - sourceChainSelectors: SnakedCell - offRampAdded: c.Address +export interface Router_LockOrBurn { + readonly $: 'Router_LockOrBurn' + queryID: uint64 + tokenPool: c.Address + request: TokenPool_LockOrBurnInV1 + executorAddress: c.Address } -export const OffRampAdded = { +export const Router_LockOrBurn = { + PREFIX: 0x6f2d00df, + create(args: { - sourceChainSelectors: SnakedCell - offRampAdded: c.Address - }): OffRampAdded { + queryID?: uint64 + tokenPool: c.Address + request: TokenPool_LockOrBurnInV1 + executorAddress: c.Address + }): Router_LockOrBurn { return { - $: 'OffRampAdded', - ...args + $: 'Router_LockOrBurn', + ...args, + queryID: args.queryID ?? 0n } }, - fromSlice(s: c.Slice): OffRampAdded { + fromSlice(s: c.Slice): Router_LockOrBurn { + loadAndCheckPrefix32(s, 0x6f2d00df, 'Router_LockOrBurn'); return { - $: 'OffRampAdded', - sourceChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), - offRampAdded: s.loadAddress(), + $: 'Router_LockOrBurn', + queryID: s.loadUintBig(64), + tokenPool: s.loadAddress(), + request: loadCellRef(s, TokenPool_LockOrBurnInV1.fromSlice), + executorAddress: s.loadAddress(), } }, - store(self: OffRampAdded, b: c.Builder): void { - storeSnakedCellOf(self.sourceChainSelectors, b, (v, b) => b.storeUint(v, 64)); - b.storeAddress(self.offRampAdded); + store(self: Router_LockOrBurn, b: c.Builder): void { + b.storeUint(0x6f2d00df, 32); + b.storeUint(self.queryID, 64); + b.storeAddress(self.tokenPool); + storeCellRef(self.request, b, TokenPool_LockOrBurnInV1.store); + b.storeAddress(self.executorAddress); }, - toCell(self: OffRampAdded): c.Cell { - return makeCellFrom(self, OffRampAdded.store); + toCell(self: Router_LockOrBurn): c.Cell { + return makeCellFrom(self, Router_LockOrBurn.store); } } /** - > struct OffRampRemoved { - > sourceChainSelectors: SnakedCell - > offRampRemoved: address + > struct (0xb19862a3) Router_RelayReleaseOrMint { + > queryID: uint64 + > sourceChainSelector: uint64 + > tokenPool: address + > request: Cell + > requestedFinalityConfig: uint32 + > replyTo: address > } */ -export interface OffRampRemoved { - readonly $: 'OffRampRemoved' - sourceChainSelectors: SnakedCell - offRampRemoved: c.Address +export interface Router_RelayReleaseOrMint { + readonly $: 'Router_RelayReleaseOrMint' + queryID: uint64 + sourceChainSelector: uint64 + tokenPool: c.Address + request: TokenPool_ReleaseOrMintInV1 + requestedFinalityConfig: uint32 + replyTo: c.Address } -export const OffRampRemoved = { +export const Router_RelayReleaseOrMint = { + PREFIX: 0xb19862a3, + create(args: { - sourceChainSelectors: SnakedCell - offRampRemoved: c.Address - }): OffRampRemoved { + queryID?: uint64 + sourceChainSelector: uint64 + tokenPool: c.Address + request: TokenPool_ReleaseOrMintInV1 + requestedFinalityConfig: uint32 + replyTo: c.Address + }): Router_RelayReleaseOrMint { return { - $: 'OffRampRemoved', - ...args + $: 'Router_RelayReleaseOrMint', + ...args, + queryID: args.queryID ?? 0n } }, - fromSlice(s: c.Slice): OffRampRemoved { + fromSlice(s: c.Slice): Router_RelayReleaseOrMint { + loadAndCheckPrefix32(s, 0xb19862a3, 'Router_RelayReleaseOrMint'); return { - $: 'OffRampRemoved', - sourceChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), - offRampRemoved: s.loadAddress(), + $: 'Router_RelayReleaseOrMint', + queryID: s.loadUintBig(64), + sourceChainSelector: s.loadUintBig(64), + tokenPool: s.loadAddress(), + request: loadCellRef(s, TokenPool_ReleaseOrMintInV1.fromSlice), + requestedFinalityConfig: s.loadUintBig(32), + replyTo: s.loadAddress(), } }, - store(self: OffRampRemoved, b: c.Builder): void { - storeSnakedCellOf(self.sourceChainSelectors, b, (v, b) => b.storeUint(v, 64)); - b.storeAddress(self.offRampRemoved); + store(self: Router_RelayReleaseOrMint, b: c.Builder): void { + b.storeUint(0xb19862a3, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.sourceChainSelector, 64); + b.storeAddress(self.tokenPool); + storeCellRef(self.request, b, TokenPool_ReleaseOrMintInV1.store); + b.storeUint(self.requestedFinalityConfig, 32); + b.storeAddress(self.replyTo); }, - toCell(self: OffRampRemoved): c.Cell { - return makeCellFrom(self, OffRampRemoved.store); + toCell(self: Router_RelayReleaseOrMint): c.Cell { + return makeCellFrom(self, Router_RelayReleaseOrMint.store); } } /** - > struct Cursed { - > subject: uint128 + > struct Router_GetValidatedFeeContext { + > routerContext: address + > userContext: RemainingBitsOrRef > } */ -export interface Cursed { - readonly $: 'Cursed' - subject: uint128 +export interface Router_GetValidatedFeeContext { + readonly $: 'Router_GetValidatedFeeContext' + routerContext: c.Address + userContext: RemainingBitsOrRef } -export const Cursed = { +export const Router_GetValidatedFeeContext = { create(args: { - subject: uint128 - }): Cursed { + routerContext: c.Address + userContext: RemainingBitsOrRef + }): Router_GetValidatedFeeContext { return { - $: 'Cursed', + $: 'Router_GetValidatedFeeContext', ...args } }, - fromSlice(s: c.Slice): Cursed { + fromSlice(s: c.Slice): Router_GetValidatedFeeContext { return { - $: 'Cursed', - subject: s.loadUintBig(128), + $: 'Router_GetValidatedFeeContext', + routerContext: s.loadAddress(), + userContext: loadTolkRemaining(s), } }, - store(self: Cursed, b: c.Builder): void { - b.storeUint(self.subject, 128); + store(self: Router_GetValidatedFeeContext, b: c.Builder): void { + b.storeAddress(self.routerContext); + storeTolkRemaining(self.userContext, b); }, - toCell(self: Cursed): c.Cell { - return makeCellFrom(self, Cursed.store); + toCell(self: Router_GetValidatedFeeContext): c.Cell { + return makeCellFrom(self, Router_GetValidatedFeeContext.store); } } /** - > struct Uncursed { - > subject: uint128 - > } + > type Router_MessageValidated_Any = Router_MessageValidated */ -export interface Uncursed { - readonly $: 'Uncursed' - subject: uint128 -} +export type Router_MessageValidated_Any = Router_MessageValidated -export const Uncursed = { - create(args: { - subject: uint128 - }): Uncursed { - return { - $: 'Uncursed', - ...args - } - }, - fromSlice(s: c.Slice): Uncursed { - return { - $: 'Uncursed', - subject: s.loadUintBig(128), - } +export const Router_MessageValidated_Any = { + fromSlice(s: c.Slice): Router_MessageValidated_Any { + return (() => { + loadAndCheckPrefix32(s, 0x9e2155ec, 'Router_MessageValidated'); + return { + $: 'Router_MessageValidated', + fee: s.loadCoins(), + msg: loadCellRef(s, Router_CCIPSend.fromSlice), + context: loadTolkRemaining(s), + } + })(); }, - store(self: Uncursed, b: c.Builder): void { - b.storeUint(self.subject, 128); + store(self: Router_MessageValidated_Any, b: c.Builder): void { + b.storeUint(0x9e2155ec, 32); + b.storeCoins(self.fee); + storeCellRef(self.msg, b, Router_CCIPSend.store); + storeTolkRemaining(self.context, b); }, - toCell(self: Uncursed): c.Cell { - return makeCellFrom(self, Uncursed.store); + toCell(self: Router_MessageValidated_Any): c.Cell { + return makeCellFrom(self, Router_MessageValidated_Any.store); } } /** - > struct MessageToOffRampBounced { - > offRamp: address - > execId: uint192 + > struct (0x9e2155ec) Router_MessageValidated { + > fee: coins + > msg: Cell + > context: RemainingBitsOrRef > } */ -export interface MessageToOffRampBounced { - readonly $: 'MessageToOffRampBounced' - offRamp: c.Address - execId: uint192 +export interface Router_MessageValidated { + readonly $: 'Router_MessageValidated' + fee: coins + msg: Router_CCIPSend + context: RemainingBitsOrRef } -export const MessageToOffRampBounced = { - create(args: { - offRamp: c.Address - execId: uint192 - }): MessageToOffRampBounced { +export const Router_MessageValidated = { + PREFIX: 0x9e2155ec, + + create(args: { + fee: coins + msg: Router_CCIPSend + context: RemainingBitsOrRef + }): Router_MessageValidated { return { - $: 'MessageToOffRampBounced', + $: 'Router_MessageValidated', ...args } }, - fromSlice(s: c.Slice): MessageToOffRampBounced { - return { - $: 'MessageToOffRampBounced', - offRamp: s.loadAddress(), - execId: s.loadUintBig(192), - } +} + +/** + > type Router_MessageValidationFailed_Any = Router_MessageValidationFailed + */ +export type Router_MessageValidationFailed_Any = Router_MessageValidationFailed + +export const Router_MessageValidationFailed_Any = { + fromSlice(s: c.Slice): Router_MessageValidationFailed_Any { + return (() => { + loadAndCheckPrefix32(s, 0xec23c562, 'Router_MessageValidationFailed'); + return { + $: 'Router_MessageValidationFailed', + error: s.loadUintBig(256), + msg: loadCellRef(s, Router_CCIPSend.fromSlice), + context: loadTolkRemaining(s), + } + })(); }, - store(self: MessageToOffRampBounced, b: c.Builder): void { - b.storeAddress(self.offRamp); - b.storeUint(self.execId, 192); + store(self: Router_MessageValidationFailed_Any, b: c.Builder): void { + b.storeUint(0xec23c562, 32); + b.storeUint(self.error, 256); + storeCellRef(self.msg, b, Router_CCIPSend.store); + storeTolkRemaining(self.context, b); }, - toCell(self: MessageToOffRampBounced): c.Cell { - return makeCellFrom(self, MessageToOffRampBounced.store); + toCell(self: Router_MessageValidationFailed_Any): c.Cell { + return makeCellFrom(self, Router_MessageValidationFailed_Any.store); } } /** - > struct TokenPool_LockOrBurnPrepared { - > feeAmount: coins - > destTokenAmount: coins - > out: TokenPool_LockOrBurnOutV1 + > struct (0xec23c562) Router_MessageValidationFailed { + > error: uint256 + > msg: Cell + > context: RemainingBitsOrRef > } */ -export interface TokenPool_LockOrBurnPrepared { - readonly $: 'TokenPool_LockOrBurnPrepared' - feeAmount: coins - destTokenAmount: coins - out: TokenPool_LockOrBurnOutV1 +export interface Router_MessageValidationFailed { + readonly $: 'Router_MessageValidationFailed' + error: uint256 + msg: Router_CCIPSend + context: RemainingBitsOrRef } -export const TokenPool_LockOrBurnPrepared = { - create(args: { - feeAmount: coins - destTokenAmount: coins - out: TokenPool_LockOrBurnOutV1 - }): TokenPool_LockOrBurnPrepared { +export const Router_MessageValidationFailed = { + PREFIX: 0xec23c562, + + create(args: { + error: uint256 + msg: Router_CCIPSend + context: RemainingBitsOrRef + }): Router_MessageValidationFailed { return { - $: 'TokenPool_LockOrBurnPrepared', + $: 'Router_MessageValidationFailed', ...args } }, - fromSlice(s: c.Slice): TokenPool_LockOrBurnPrepared { - return { - $: 'TokenPool_LockOrBurnPrepared', - feeAmount: s.loadCoins(), - destTokenAmount: s.loadCoins(), - out: TokenPool_LockOrBurnOutV1.fromSlice(s), - } - }, - store(self: TokenPool_LockOrBurnPrepared, b: c.Builder): void { - b.storeCoins(self.feeAmount); - b.storeCoins(self.destTokenAmount); - TokenPool_LockOrBurnOutV1.store(self.out, b); - }, - toCell(self: TokenPool_LockOrBurnPrepared): c.Cell { - return makeCellFrom(self, TokenPool_LockOrBurnPrepared.store); - } } /** - > struct TokenPool_Transfer { - > id: uint256 - > details: Cell> + > struct (0xb76e3a84) Router_TokenPoolLockOrBurnFailed { + > queryID: uint64 + > tokenPool: address + > exitCode: int32 > } */ -export interface TokenPool_Transfer { - readonly $: 'TokenPool_Transfer' - id: uint256 - details: TokenPool_TransferDetails +export interface Router_TokenPoolLockOrBurnFailed { + readonly $: 'Router_TokenPoolLockOrBurnFailed' + queryID: uint64 + tokenPool: c.Address + exitCode: int32 } -export const TokenPool_Transfer = { - create(args: { - id: uint256 - details: TokenPool_TransferDetails - }): TokenPool_Transfer { +export const Router_TokenPoolLockOrBurnFailed = { + PREFIX: 0xb76e3a84, + + create(args: { + queryID?: uint64 + tokenPool: c.Address + exitCode: int32 + }): Router_TokenPoolLockOrBurnFailed { return { - $: 'TokenPool_Transfer', - ...args + $: 'Router_TokenPoolLockOrBurnFailed', + ...args, + queryID: args.queryID ?? 0n + } + }, + fromSlice(s: c.Slice): Router_TokenPoolLockOrBurnFailed { + loadAndCheckPrefix32(s, 0xb76e3a84, 'Router_TokenPoolLockOrBurnFailed'); + return { + $: 'Router_TokenPoolLockOrBurnFailed', + queryID: s.loadUintBig(64), + tokenPool: s.loadAddress(), + exitCode: s.loadIntBig(32), } }, + store(self: Router_TokenPoolLockOrBurnFailed, b: c.Builder): void { + b.storeUint(0xb76e3a84, 32); + b.storeUint(self.queryID, 64); + b.storeAddress(self.tokenPool); + b.storeInt(self.exitCode, 32); + }, + toCell(self: Router_TokenPoolLockOrBurnFailed): c.Cell { + return makeCellFrom(self, Router_TokenPoolLockOrBurnFailed.store); + } } /** - > struct TokenPool_TransferDetails { - > receiver: R - > remoteChainSelector: uint64 - > originalSender: S - > amount: C - > localToken: address + > struct (0xb980cb4e) Router_TokenPoolReleaseOrMintFailed { + > queryID: uint64 + > sourceChainSelector: uint64 + > replyTo: address + > tokenPool: address + > exitCode: int32 > } */ -export interface TokenPool_TransferDetails { - readonly $: 'TokenPool_TransferDetails' - receiver: R - remoteChainSelector: uint64 - originalSender: S - amount: C - localToken: c.Address +export interface Router_TokenPoolReleaseOrMintFailed { + readonly $: 'Router_TokenPoolReleaseOrMintFailed' + queryID: uint64 + sourceChainSelector: uint64 + replyTo: c.Address + tokenPool: c.Address + exitCode: int32 } -export const TokenPool_TransferDetails = { - create(args: { - receiver: R - remoteChainSelector: uint64 - originalSender: S - amount: C - localToken: c.Address - }): TokenPool_TransferDetails { +export const Router_TokenPoolReleaseOrMintFailed = { + PREFIX: 0xb980cb4e, + + create(args: { + queryID?: uint64 + sourceChainSelector: uint64 + replyTo: c.Address + tokenPool: c.Address + exitCode: int32 + }): Router_TokenPoolReleaseOrMintFailed { return { - $: 'TokenPool_TransferDetails', - ...args + $: 'Router_TokenPoolReleaseOrMintFailed', + ...args, + queryID: args.queryID ?? 0n + } + }, + fromSlice(s: c.Slice): Router_TokenPoolReleaseOrMintFailed { + loadAndCheckPrefix32(s, 0xb980cb4e, 'Router_TokenPoolReleaseOrMintFailed'); + return { + $: 'Router_TokenPoolReleaseOrMintFailed', + queryID: s.loadUintBig(64), + sourceChainSelector: s.loadUintBig(64), + replyTo: s.loadAddress(), + tokenPool: s.loadAddress(), + exitCode: s.loadIntBig(32), } }, + store(self: Router_TokenPoolReleaseOrMintFailed, b: c.Builder): void { + b.storeUint(0xb980cb4e, 32); + b.storeUint(self.queryID, 64); + b.storeUint(self.sourceChainSelector, 64); + b.storeAddress(self.replyTo); + b.storeAddress(self.tokenPool); + b.storeInt(self.exitCode, 32); + }, + toCell(self: Router_TokenPoolReleaseOrMintFailed): c.Cell { + return makeCellFrom(self, Router_TokenPoolReleaseOrMintFailed.store); + } } /** - > type TokenPool_LockOrBurnTransfer = TokenPool_Transfer, coins> + > struct RMNRemote { + > admin: Ownable2Step + > cursedSubjects: CursedSubjects + > forwardUpdates: map + > } */ -export type TokenPool_LockOrBurnTransfer = TokenPool_Transfer +export interface RMNRemote { + readonly $: 'RMNRemote' + admin: Ownable2Step + cursedSubjects: CursedSubjects + forwardUpdates: Set /* = [] as map */ +} -export const TokenPool_LockOrBurnTransfer = { - fromSlice(s: c.Slice): TokenPool_LockOrBurnTransfer { - return (() => { - return { - $: 'TokenPool_Transfer', - id: s.loadUintBig(256), - details: loadCellRef>(s, - (s) => (() => { - return { - $: 'TokenPool_TransferDetails', - receiver: loadCellRef(s, CrossChainAddress.fromSlice), - remoteChainSelector: s.loadUintBig(64), - originalSender: s.loadAddress(), - amount: s.loadCoins(), - localToken: s.loadAddress(), - } - })() - ), - } - })(); +export const RMNRemote = { + create(args: { + admin: Ownable2Step + cursedSubjects: CursedSubjects + forwardUpdates: Set /* = [] as map */ + }): RMNRemote { + return { + $: 'RMNRemote', + ...args + } }, - store(self: TokenPool_LockOrBurnTransfer, b: c.Builder): void { - b.storeUint(self.id, 256); - storeCellRef>(self.details, b, - (v,b) => { storeCellRef(v.receiver, b, CrossChainAddress.store); - b.storeUint(v.remoteChainSelector, 64); - b.storeAddress(v.originalSender); - b.storeCoins(v.amount); - b.storeAddress(v.localToken); } - ); + fromSlice(s: c.Slice): RMNRemote { + return { + $: 'RMNRemote', + admin: Ownable2Step.fromSlice(s), + cursedSubjects: CursedSubjects.fromSlice(s), + forwardUpdates: dictToSet(c.Dictionary.load(c.Dictionary.Keys.Address(), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), + } }, - toCell(self: TokenPool_LockOrBurnTransfer): c.Cell { - return makeCellFrom(self, TokenPool_LockOrBurnTransfer.store); + store(self: RMNRemote, b: c.Builder): void { + Ownable2Step.store(self.admin, b); + CursedSubjects.store(self.cursedSubjects, b); + b.storeDict(setToDict(self.forwardUpdates, c.Dictionary.Keys.Address(), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.Address(), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); + }, + toCell(self: RMNRemote): c.Cell { + return makeCellFrom(self, RMNRemote.store); } } /** - > struct TokenPool_LockOrBurnInV1 { - > transfer: TokenPool_LockOrBurnTransfer + > struct Storage { + > id: uint32 + > ownable: Ownable2Step + > wrappedNative: address + > onRamps: map + > offRamps: map + > rmnRemote: Cell > } */ -export interface TokenPool_LockOrBurnInV1 { - readonly $: 'TokenPool_LockOrBurnInV1' - transfer: TokenPool_LockOrBurnTransfer +export interface Storage { + readonly $: 'Storage' + id: uint32 + ownable: Ownable2Step + wrappedNative: c.Address + onRamps: Map /* = [] as map */ + offRamps: Map /* = [] as map */ + rmnRemote: RMNRemote } -export const TokenPool_LockOrBurnInV1 = { +export const Storage = { create(args: { - transfer: TokenPool_LockOrBurnTransfer - }): TokenPool_LockOrBurnInV1 { + id: uint32 + ownable: Ownable2Step + wrappedNative: c.Address + onRamps: Map /* = [] as map */ + offRamps: Map /* = [] as map */ + rmnRemote: RMNRemote + }): Storage { return { - $: 'TokenPool_LockOrBurnInV1', + $: 'Storage', ...args } }, - fromSlice(s: c.Slice): TokenPool_LockOrBurnInV1 { + fromSlice(s: c.Slice): Storage { return { - $: 'TokenPool_LockOrBurnInV1', - transfer: TokenPool_LockOrBurnTransfer.fromSlice(s), + $: 'Storage', + id: s.loadUintBig(32), + ownable: Ownable2Step.fromSlice(s), + wrappedNative: s.loadAddress(), + onRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( + (s) => s.loadAddress(), + (v,b) => b.storeAddress(v) + ), s)), + offRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( + (s) => s.loadAddress(), + (v,b) => b.storeAddress(v) + ), s)), + rmnRemote: loadCellRef(s, RMNRemote.fromSlice), } }, - store(self: TokenPool_LockOrBurnInV1, b: c.Builder): void { - TokenPool_LockOrBurnTransfer.store(self.transfer, b); + store(self: Storage, b: c.Builder): void { + b.storeUint(self.id, 32); + Ownable2Step.store(self.ownable, b); + b.storeAddress(self.wrappedNative); + b.storeDict(mapToDict(self.onRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( + (s) => s.loadAddress(), + (v,b) => b.storeAddress(v) + )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( + (s) => s.loadAddress(), + (v,b) => b.storeAddress(v) + )); + b.storeDict(mapToDict(self.offRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( + (s) => s.loadAddress(), + (v,b) => b.storeAddress(v) + )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( + (s) => s.loadAddress(), + (v,b) => b.storeAddress(v) + )); + storeCellRef(self.rmnRemote, b, RMNRemote.store); }, - toCell(self: TokenPool_LockOrBurnInV1): c.Cell { - return makeCellFrom(self, TokenPool_LockOrBurnInV1.store); + toCell(self: Storage): c.Cell { + return makeCellFrom(self, Storage.store); } } /** - > struct TokenPool_LockOrBurnOutV1 { - > destTokenAddress: Cell - > destPoolData: cell + > struct OnRampSet { + > destChainSelectors: SnakedCell + > onRamp: address? > } */ -export interface TokenPool_LockOrBurnOutV1 { - readonly $: 'TokenPool_LockOrBurnOutV1' - destTokenAddress: CrossChainAddress - destPoolData: c.Cell +export interface OnRampSet { + readonly $: 'OnRampSet' + destChainSelectors: SnakedCell + onRamp: c.Address | null } -export const TokenPool_LockOrBurnOutV1 = { +export const OnRampSet = { create(args: { - destTokenAddress: CrossChainAddress - destPoolData: c.Cell - }): TokenPool_LockOrBurnOutV1 { + destChainSelectors: SnakedCell + onRamp: c.Address | null + }): OnRampSet { return { - $: 'TokenPool_LockOrBurnOutV1', + $: 'OnRampSet', ...args } }, - fromSlice(s: c.Slice): TokenPool_LockOrBurnOutV1 { + fromSlice(s: c.Slice): OnRampSet { return { - $: 'TokenPool_LockOrBurnOutV1', - destTokenAddress: loadCellRef(s, CrossChainAddress.fromSlice), - destPoolData: s.loadRef(), + $: 'OnRampSet', + destChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), + onRamp: s.loadMaybeAddress(), } }, - store(self: TokenPool_LockOrBurnOutV1, b: c.Builder): void { - storeCellRef(self.destTokenAddress, b, CrossChainAddress.store); - b.storeRef(self.destPoolData); + store(self: OnRampSet, b: c.Builder): void { + storeSnakedCellOf(self.destChainSelectors, b, (v, b) => b.storeUint(v, 64)); + b.storeAddress(self.onRamp); }, - toCell(self: TokenPool_LockOrBurnOutV1): c.Cell { - return makeCellFrom(self, TokenPool_LockOrBurnOutV1.store); + toCell(self: OnRampSet): c.Cell { + return makeCellFrom(self, OnRampSet.store); } } /** - > type CrossChainAddress = slice + > struct OffRampAdded { + > sourceChainSelectors: SnakedCell + > offRampAdded: address + > } */ -export type CrossChainAddress = c.Slice +export interface OffRampAdded { + readonly $: 'OffRampAdded' + sourceChainSelectors: SnakedCell + offRampAdded: c.Address +} -export const CrossChainAddress = { - fromSlice(s: c.Slice): CrossChainAddress { - return invokeCustomUnpackFromSlice('CrossChainAddress', s); +export const OffRampAdded = { + create(args: { + sourceChainSelectors: SnakedCell + offRampAdded: c.Address + }): OffRampAdded { + return { + $: 'OffRampAdded', + ...args + } }, - store(self: CrossChainAddress, b: c.Builder): void { - invokeCustomPackToBuilder('CrossChainAddress', self, b); + fromSlice(s: c.Slice): OffRampAdded { + return { + $: 'OffRampAdded', + sourceChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), + offRampAdded: s.loadAddress(), + } }, - toCell(self: CrossChainAddress): c.Cell { - return makeCellFrom(self, CrossChainAddress.store); + store(self: OffRampAdded, b: c.Builder): void { + storeSnakedCellOf(self.sourceChainSelectors, b, (v, b) => b.storeUint(v, 64)); + b.storeAddress(self.offRampAdded); + }, + toCell(self: OffRampAdded): c.Cell { + return makeCellFrom(self, OffRampAdded.store); } } /** - > type ExtraArgs = GenericExtraArgsV2 | SVMExtraArgsV1 | SuiExtraArgsV1 + > struct OffRampRemoved { + > sourceChainSelectors: SnakedCell + > offRampRemoved: address + > } */ -export type ExtraArgs = - | GenericExtraArgsV2 - | SVMExtraArgsV1 - | SuiExtraArgsV1 +export interface OffRampRemoved { + readonly $: 'OffRampRemoved' + sourceChainSelectors: SnakedCell + offRampRemoved: c.Address +} -export const ExtraArgs = { - fromSlice(s: c.Slice): ExtraArgs { - return lookupPrefix(s, 0x181dcf10, 32) ? GenericExtraArgsV2.fromSlice(s) : - lookupPrefix(s, 0x1f3b3aba, 32) ? SVMExtraArgsV1.fromSlice(s) : - lookupPrefix(s, 0x21ea4ca9, 32) ? SuiExtraArgsV1.fromSlice(s) : - throwNonePrefixMatch('ExtraArgs'); +export const OffRampRemoved = { + create(args: { + sourceChainSelectors: SnakedCell + offRampRemoved: c.Address + }): OffRampRemoved { + return { + $: 'OffRampRemoved', + ...args + } }, - store(self: ExtraArgs, b: c.Builder): void { - switch (self.$) { - case 'GenericExtraArgsV2': - GenericExtraArgsV2.store(self, b); - break; - case 'SVMExtraArgsV1': - SVMExtraArgsV1.store(self, b); - break; - case 'SuiExtraArgsV1': - SuiExtraArgsV1.store(self, b); - break; + fromSlice(s: c.Slice): OffRampRemoved { + return { + $: 'OffRampRemoved', + sourceChainSelectors: loadSnakedCellOf(s, (s) => s.loadUintBig(64)), + offRampRemoved: s.loadAddress(), } }, - toCell(self: ExtraArgs): c.Cell { - return makeCellFrom(self, ExtraArgs.store); + store(self: OffRampRemoved, b: c.Builder): void { + storeSnakedCellOf(self.sourceChainSelectors, b, (v, b) => b.storeUint(v, 64)); + b.storeAddress(self.offRampRemoved); + }, + toCell(self: OffRampRemoved): c.Cell { + return makeCellFrom(self, OffRampRemoved.store); } } /** - > struct (0x181dcf10) GenericExtraArgsV2 { - > gasLimit: uint256? - > allowOutOfOrderExecution: bool + > struct Cursed { + > subject: uint128 > } */ -export interface GenericExtraArgsV2 { - readonly $: 'GenericExtraArgsV2' - gasLimit: uint256 | null - allowOutOfOrderExecution: boolean +export interface Cursed { + readonly $: 'Cursed' + subject: uint128 } -export const GenericExtraArgsV2 = { - PREFIX: 0x181dcf10, - +export const Cursed = { create(args: { - gasLimit: uint256 | null - allowOutOfOrderExecution: boolean - }): GenericExtraArgsV2 { + subject: uint128 + }): Cursed { return { - $: 'GenericExtraArgsV2', + $: 'Cursed', ...args } }, - fromSlice(s: c.Slice): GenericExtraArgsV2 { - loadAndCheckPrefix32(s, 0x181dcf10, 'GenericExtraArgsV2'); + fromSlice(s: c.Slice): Cursed { return { - $: 'GenericExtraArgsV2', - gasLimit: s.loadBoolean() ? s.loadUintBig(256) : null, - allowOutOfOrderExecution: s.loadBoolean(), + $: 'Cursed', + subject: s.loadUintBig(128), } }, - store(self: GenericExtraArgsV2, b: c.Builder): void { - b.storeUint(0x181dcf10, 32); - storeTolkNullable(self.gasLimit, b, - (v,b) => b.storeUint(v, 256) - ); - b.storeBit(self.allowOutOfOrderExecution); + store(self: Cursed, b: c.Builder): void { + b.storeUint(self.subject, 128); }, - toCell(self: GenericExtraArgsV2): c.Cell { - return makeCellFrom(self, GenericExtraArgsV2.store); + toCell(self: Cursed): c.Cell { + return makeCellFrom(self, Cursed.store); } } /** - > struct (0x1f3b3aba) SVMExtraArgsV1 { - > computeUnits: uint32 - > accountIsWritableBitmap: uint64 - > allowOutOfOrderExecution: bool - > tokenReceiver: uint256 - > accounts: SnakedCell + > struct Uncursed { + > subject: uint128 > } */ -export interface SVMExtraArgsV1 { - readonly $: 'SVMExtraArgsV1' - computeUnits: uint32 - accountIsWritableBitmap: uint64 - allowOutOfOrderExecution: boolean - tokenReceiver: uint256 - accounts: SnakedCell +export interface Uncursed { + readonly $: 'Uncursed' + subject: uint128 } -export const SVMExtraArgsV1 = { - PREFIX: 0x1f3b3aba, - +export const Uncursed = { create(args: { - computeUnits: uint32 - accountIsWritableBitmap: uint64 - allowOutOfOrderExecution: boolean - tokenReceiver: uint256 - accounts: SnakedCell - }): SVMExtraArgsV1 { + subject: uint128 + }): Uncursed { return { - $: 'SVMExtraArgsV1', + $: 'Uncursed', ...args } }, - fromSlice(s: c.Slice): SVMExtraArgsV1 { - loadAndCheckPrefix32(s, 0x1f3b3aba, 'SVMExtraArgsV1'); + fromSlice(s: c.Slice): Uncursed { return { - $: 'SVMExtraArgsV1', - computeUnits: s.loadUintBig(32), - accountIsWritableBitmap: s.loadUintBig(64), - allowOutOfOrderExecution: s.loadBoolean(), - tokenReceiver: s.loadUintBig(256), - accounts: loadSnakedCellOf(s, (s) => s.loadUintBig(256)), + $: 'Uncursed', + subject: s.loadUintBig(128), } }, - store(self: SVMExtraArgsV1, b: c.Builder): void { - b.storeUint(0x1f3b3aba, 32); - b.storeUint(self.computeUnits, 32); - b.storeUint(self.accountIsWritableBitmap, 64); - b.storeBit(self.allowOutOfOrderExecution); - b.storeUint(self.tokenReceiver, 256); - storeSnakedCellOf(self.accounts, b, (v, b) => b.storeUint(v, 256)); + store(self: Uncursed, b: c.Builder): void { + b.storeUint(self.subject, 128); }, - toCell(self: SVMExtraArgsV1): c.Cell { - return makeCellFrom(self, SVMExtraArgsV1.store); + toCell(self: Uncursed): c.Cell { + return makeCellFrom(self, Uncursed.store); } } /** - > struct (0x21ea4ca9) SuiExtraArgsV1 { - > gasLimit: uint256 - > allowOutOfOrderExecution: bool - > tokenReceiver: uint256 - > receiverObjectIds: SnakedCell + > struct MessageToOffRampBounced { + > offRamp: address + > execId: uint192 > } */ -export interface SuiExtraArgsV1 { - readonly $: 'SuiExtraArgsV1' - gasLimit: uint256 - allowOutOfOrderExecution: boolean - tokenReceiver: uint256 - receiverObjectIds: SnakedCell +export interface MessageToOffRampBounced { + readonly $: 'MessageToOffRampBounced' + offRamp: c.Address + execId: uint192 } -export const SuiExtraArgsV1 = { - PREFIX: 0x21ea4ca9, - +export const MessageToOffRampBounced = { create(args: { - gasLimit: uint256 - allowOutOfOrderExecution: boolean - tokenReceiver: uint256 - receiverObjectIds: SnakedCell - }): SuiExtraArgsV1 { + offRamp: c.Address + execId: uint192 + }): MessageToOffRampBounced { return { - $: 'SuiExtraArgsV1', + $: 'MessageToOffRampBounced', ...args } }, - fromSlice(s: c.Slice): SuiExtraArgsV1 { - loadAndCheckPrefix32(s, 0x21ea4ca9, 'SuiExtraArgsV1'); + fromSlice(s: c.Slice): MessageToOffRampBounced { return { - $: 'SuiExtraArgsV1', - gasLimit: s.loadUintBig(256), - allowOutOfOrderExecution: s.loadBoolean(), - tokenReceiver: s.loadUintBig(256), - receiverObjectIds: loadSnakedCellOf(s, (s) => s.loadUintBig(256)), + $: 'MessageToOffRampBounced', + offRamp: s.loadAddress(), + execId: s.loadUintBig(192), } }, - store(self: SuiExtraArgsV1, b: c.Builder): void { - b.storeUint(0x21ea4ca9, 32); - b.storeUint(self.gasLimit, 256); - b.storeBit(self.allowOutOfOrderExecution); - b.storeUint(self.tokenReceiver, 256); - storeSnakedCellOf(self.receiverObjectIds, b, (v, b) => b.storeUint(v, 256)); + store(self: MessageToOffRampBounced, b: c.Builder): void { + b.storeAddress(self.offRamp); + b.storeUint(self.execId, 192); }, - toCell(self: SuiExtraArgsV1): c.Cell { - return makeCellFrom(self, SuiExtraArgsV1.store); + toCell(self: MessageToOffRampBounced): c.Cell { + return makeCellFrom(self, MessageToOffRampBounced.store); } } /** - > struct TokenAmount { - > amount: coins - > token: address + > struct TokenPoolWithdrawBounced { + > wallet: address + > tokenPool: address + > queryId: uint64 + > exitCode: int32 > } */ -export interface TokenAmount { - readonly $: 'TokenAmount' - amount: coins - token: c.Address +export interface TokenPoolWithdrawBounced { + readonly $: 'TokenPoolWithdrawBounced' + wallet: c.Address + tokenPool: c.Address + queryId: uint64 + exitCode: int32 } -export const TokenAmount = { +export const TokenPoolWithdrawBounced = { create(args: { - amount: coins - token: c.Address - }): TokenAmount { + wallet: c.Address + tokenPool: c.Address + queryId?: uint64 + exitCode: int32 + }): TokenPoolWithdrawBounced { return { - $: 'TokenAmount', - ...args + $: 'TokenPoolWithdrawBounced', + ...args, + queryId: args.queryId ?? 0n } }, - fromSlice(s: c.Slice): TokenAmount { + fromSlice(s: c.Slice): TokenPoolWithdrawBounced { return { - $: 'TokenAmount', - amount: s.loadCoins(), - token: s.loadAddress(), + $: 'TokenPoolWithdrawBounced', + wallet: s.loadAddress(), + tokenPool: s.loadAddress(), + queryId: s.loadUintBig(64), + exitCode: s.loadIntBig(32), } }, - store(self: TokenAmount, b: c.Builder): void { - b.storeCoins(self.amount); - b.storeAddress(self.token); + store(self: TokenPoolWithdrawBounced, b: c.Builder): void { + b.storeAddress(self.wallet); + b.storeAddress(self.tokenPool); + b.storeUint(self.queryId, 64); + b.storeInt(self.exitCode, 32); }, - toCell(self: TokenAmount): c.Cell { - return makeCellFrom(self, TokenAmount.store); + toCell(self: TokenPoolWithdrawBounced): c.Cell { + return makeCellFrom(self, TokenPoolWithdrawBounced.store); } } @@ -3377,7 +3744,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class Router implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -3401,6 +3768,7 @@ export class Router implements c.Contract { 'Router_Error.InsufficientFee': 57109, 'Router_Error.TokenTransferNotThroughNotification': 57110, 'Router_Error.TokenAmountMismatch': 57112, + 'Router_Error.SourceChainSelectorMismatch': 57114, } readonly address: c.Address @@ -3565,6 +3933,17 @@ export class Router implements c.Contract { return Router_LockOrBurn.toCell(Router_LockOrBurn.create(body)); } + static createCellOfRouterRelayReleaseOrMint(body: { + queryID?: uint64 + sourceChainSelector: uint64 + tokenPool: c.Address + request: TokenPool_ReleaseOrMintInV1 + requestedFinalityConfig: uint32 + replyTo: c.Address + }) { + return Router_RelayReleaseOrMint.toCell(Router_RelayReleaseOrMint.create(body)); + } + static createCellOfRouterRMNOwnableMessage(body: { content: RemainingBitsAndRefs }) { @@ -3796,6 +4175,21 @@ export class Router implements c.Contract { }); } + async sendRouterRelayReleaseOrMint(provider: ContractProvider, via: Sender, msgValue: coins, body: { + queryID?: uint64 + sourceChainSelector: uint64 + tokenPool: c.Address + request: TokenPool_ReleaseOrMintInV1 + requestedFinalityConfig: uint32 + replyTo: c.Address + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: Router_RelayReleaseOrMint.toCell(Router_RelayReleaseOrMint.create(body)), + ...extraOptions + }); + } + async sendRouterRMNOwnableMessage(provider: ContractProvider, via: Sender, msgValue: coins, body: { content: RemainingBitsAndRefs }, extraOptions?: ExtraSendOptions) { diff --git a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts index 689ef33bd..74934502b 100644 --- a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts @@ -1389,7 +1389,7 @@ export const CursedSubjects = { /** > struct TokenPool_AdminConfig { > ownable: Cell - > rmnProxy: address + > rmnProxy: address? > dynamicConfig: Cell > jettonClient: JettonClient > allowedFinalityConfig: uint32 @@ -1400,7 +1400,7 @@ export const CursedSubjects = { export interface TokenPool_AdminConfig { readonly $: 'TokenPool_AdminConfig' ownable: Ownable2Step - rmnProxy: c.Address + rmnProxy: c.Address | null dynamicConfig: TokenPool_DynamicConfig jettonClient: JettonClient allowedFinalityConfig: uint32 /* = 0 as uint32 */ @@ -1411,7 +1411,7 @@ export interface TokenPool_AdminConfig { export const TokenPool_AdminConfig = { create(args: { ownable: Ownable2Step - rmnProxy: c.Address + rmnProxy: c.Address | null dynamicConfig: TokenPool_DynamicConfig jettonClient: JettonClient allowedFinalityConfig?: uint32 /* = 0 as uint32 */ @@ -1430,7 +1430,7 @@ export const TokenPool_AdminConfig = { return { $: 'TokenPool_AdminConfig', ownable: loadCellRef(s, Ownable2Step.fromSlice), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), dynamicConfig: loadCellRef(s, TokenPool_DynamicConfig.fromSlice), jettonClient: JettonClient.fromSlice(s), allowedFinalityConfig: s.loadUintBig(32), @@ -1457,7 +1457,7 @@ export const TokenPool_AdminConfig = { /** > struct TokenPool_Data { > adminConfig: Cell - > mirroredPolicy: Cell + > localPolicy: Cell > tokenDecimals: uint8 > remoteChainConfigs: map > tokenTransferFeeConfigs: map @@ -1466,7 +1466,7 @@ export const TokenPool_AdminConfig = { export interface TokenPool_Data { readonly $: 'TokenPool_Data' adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -1475,7 +1475,7 @@ export interface TokenPool_Data { export const TokenPool_Data = { create(args: { adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -1489,7 +1489,7 @@ export const TokenPool_Data = { return { $: 'TokenPool_Data', adminConfig: loadCellRef(s, TokenPool_AdminConfig.fromSlice), - mirroredPolicy: loadCellRef(s, TokenPool_MirroredPolicy.fromSlice), + localPolicy: loadCellRef(s, TokenPool_LocalPolicy.fromSlice), tokenDecimals: s.loadUintBig(8), remoteChainConfigs: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store), s)), tokenTransferFeeConfigs: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store), s)), @@ -1497,7 +1497,7 @@ export const TokenPool_Data = { }, store(self: TokenPool_Data, b: c.Builder): void { storeCellRef(self.adminConfig, b, TokenPool_AdminConfig.store); - storeCellRef(self.mirroredPolicy, b, TokenPool_MirroredPolicy.store); + storeCellRef(self.localPolicy, b, TokenPool_LocalPolicy.store); b.storeUint(self.tokenDecimals, 8); b.storeDict(mapToDict(self.remoteChainConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store)), c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store)); b.storeDict(mapToDict(self.tokenTransferFeeConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store)), c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store)); @@ -1512,7 +1512,7 @@ export const TokenPool_Data = { > router: address > rateLimitAdmin: address? > feeAdmin: address? - > allowedDepositNamespaces: map + > allowedDepositNamespaces: map > } */ export interface TokenPool_DynamicConfig { @@ -1520,7 +1520,7 @@ export interface TokenPool_DynamicConfig { router: c.Address rateLimitAdmin: c.Address | null /* = null */ feeAdmin: c.Address | null - allowedDepositNamespaces: Map /* = [] as map */ + allowedDepositNamespaces: Set /* = [] as map */ } export const TokenPool_DynamicConfig = { @@ -1528,7 +1528,7 @@ export const TokenPool_DynamicConfig = { router: c.Address rateLimitAdmin?: c.Address | null /* = null */ feeAdmin: c.Address | null - allowedDepositNamespaces: Map /* = [] as map */ + allowedDepositNamespaces: Set /* = [] as map */ }): TokenPool_DynamicConfig { return { $: 'TokenPool_DynamicConfig', @@ -1542,14 +1542,23 @@ export const TokenPool_DynamicConfig = { router: s.loadAddress(), rateLimitAdmin: s.loadMaybeAddress(), feeAdmin: s.loadMaybeAddress(), - allowedDepositNamespaces: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool(), s)), + allowedDepositNamespaces: dictToSet(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), } }, store(self: TokenPool_DynamicConfig, b: c.Builder): void { b.storeAddress(self.router); b.storeAddress(self.rateLimitAdmin); b.storeAddress(self.feeAdmin); - b.storeDict(mapToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()), c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()); + b.storeDict(setToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); }, toCell(self: TokenPool_DynamicConfig): c.Cell { return makeCellFrom(self, TokenPool_DynamicConfig.store); @@ -1557,108 +1566,35 @@ export const TokenPool_DynamicConfig = { } /** - > struct TokenPool_MirroredPolicy { - > onRamps: map - > offRamps: map + > struct TokenPool_LocalPolicy { > cursedSubjects: CursedSubjects > } */ -export interface TokenPool_MirroredPolicy { - readonly $: 'TokenPool_MirroredPolicy' - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ +export interface TokenPool_LocalPolicy { + readonly $: 'TokenPool_LocalPolicy' cursedSubjects: CursedSubjects } -export const TokenPool_MirroredPolicy = { +export const TokenPool_LocalPolicy = { create(args: { - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ cursedSubjects: CursedSubjects - }): TokenPool_MirroredPolicy { + }): TokenPool_LocalPolicy { return { - $: 'TokenPool_MirroredPolicy', + $: 'TokenPool_LocalPolicy', ...args } }, - fromSlice(s: c.Slice): TokenPool_MirroredPolicy { + fromSlice(s: c.Slice): TokenPool_LocalPolicy { return { - $: 'TokenPool_MirroredPolicy', - onRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), - offRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), + $: 'TokenPool_LocalPolicy', cursedSubjects: CursedSubjects.fromSlice(s), } }, - store(self: TokenPool_MirroredPolicy, b: c.Builder): void { - b.storeDict(mapToDict(self.onRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); - b.storeDict(mapToDict(self.offRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); + store(self: TokenPool_LocalPolicy, b: c.Builder): void { CursedSubjects.store(self.cursedSubjects, b); }, - toCell(self: TokenPool_MirroredPolicy): c.Cell { - return makeCellFrom(self, TokenPool_MirroredPolicy.store); - } -} - -/** - > struct TokenPool_RampUpdate { - > remoteChainSelector: uint64 - > onRamp: address? - > offRamp: address? - > } - */ -export interface TokenPool_RampUpdate { - readonly $: 'TokenPool_RampUpdate' - remoteChainSelector: uint64 - onRamp: c.Address | null /* = null */ - offRamp: c.Address | null /* = null */ -} - -export const TokenPool_RampUpdate = { - create(args: { - remoteChainSelector: uint64 - onRamp?: c.Address | null /* = null */ - offRamp?: c.Address | null /* = null */ - }): TokenPool_RampUpdate { - return { - $: 'TokenPool_RampUpdate', - onRamp: null, - offRamp: null, - ...args - } - }, - fromSlice(s: c.Slice): TokenPool_RampUpdate { - return { - $: 'TokenPool_RampUpdate', - remoteChainSelector: s.loadUintBig(64), - onRamp: s.loadMaybeAddress(), - offRamp: s.loadMaybeAddress(), - } - }, - store(self: TokenPool_RampUpdate, b: c.Builder): void { - b.storeUint(self.remoteChainSelector, 64); - b.storeAddress(self.onRamp); - b.storeAddress(self.offRamp); - }, - toCell(self: TokenPool_RampUpdate): c.Cell { - return makeCellFrom(self, TokenPool_RampUpdate.store); + toCell(self: TokenPool_LocalPolicy): c.Cell { + return makeCellFrom(self, TokenPool_LocalPolicy.store); } } @@ -2771,13 +2707,13 @@ export const TokenPool_DeployableCodeSet = { /** > struct (0x84384142) TokenPool_SetAllowedDepositNamespaces { > queryId: uint64 - > allowedDepositNamespaces: map + > allowedDepositNamespaces: map > } */ export interface TokenPool_SetAllowedDepositNamespaces { readonly $: 'TokenPool_SetAllowedDepositNamespaces' queryId: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set } export const TokenPool_SetAllowedDepositNamespaces = { @@ -2785,7 +2721,7 @@ export const TokenPool_SetAllowedDepositNamespaces = { create(args: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }): TokenPool_SetAllowedDepositNamespaces { return { $: 'TokenPool_SetAllowedDepositNamespaces', @@ -2798,13 +2734,22 @@ export const TokenPool_SetAllowedDepositNamespaces = { return { $: 'TokenPool_SetAllowedDepositNamespaces', queryId: s.loadUintBig(64), - allowedDepositNamespaces: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool(), s)), + allowedDepositNamespaces: dictToSet(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), } }, store(self: TokenPool_SetAllowedDepositNamespaces, b: c.Builder): void { b.storeUint(0x84384142, 32); b.storeUint(self.queryId, 64); - b.storeDict(mapToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()), c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()); + b.storeDict(setToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); }, toCell(self: TokenPool_SetAllowedDepositNamespaces): c.Cell { return makeCellFrom(self, TokenPool_SetAllowedDepositNamespaces.store); @@ -2940,59 +2885,16 @@ export const TokenPool_ApplyTokenTransferFeeConfigUpdates = { } } -/** - > struct (0xe30764be) TokenPool_UpdateRampAccess { - > queryId: uint64 - > updates: SnakedCell - > } - */ -export interface TokenPool_UpdateRampAccess { - readonly $: 'TokenPool_UpdateRampAccess' - queryId: uint64 - updates: SnakedCell -} - -export const TokenPool_UpdateRampAccess = { - PREFIX: 0xe30764be, - - create(args: { - queryId?: uint64 - updates: SnakedCell - }): TokenPool_UpdateRampAccess { - return { - $: 'TokenPool_UpdateRampAccess', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_UpdateRampAccess { - loadAndCheckPrefix32(s, 0xe30764be, 'TokenPool_UpdateRampAccess'); - return { - $: 'TokenPool_UpdateRampAccess', - queryId: s.loadUintBig(64), - updates: loadSnakedCellOf(s, TokenPool_RampUpdate.fromSlice), - } - }, - store(self: TokenPool_UpdateRampAccess, b: c.Builder): void { - b.storeUint(0xe30764be, 32); - b.storeUint(self.queryId, 64); - storeSnakedCellOf(self.updates, b, TokenPool_RampUpdate.store); - }, - toCell(self: TokenPool_UpdateRampAccess): c.Cell { - return makeCellFrom(self, TokenPool_UpdateRampAccess.store); - } -} - /** > struct (0x9929b642) TokenPool_SetRMNProxy { > queryId: uint64 - > rmnProxy: address + > rmnProxy: address? > } */ export interface TokenPool_SetRMNProxy { readonly $: 'TokenPool_SetRMNProxy' queryId: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null } export const TokenPool_SetRMNProxy = { @@ -3000,7 +2902,7 @@ export const TokenPool_SetRMNProxy = { create(args: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }): TokenPool_SetRMNProxy { return { $: 'TokenPool_SetRMNProxy', @@ -3013,7 +2915,7 @@ export const TokenPool_SetRMNProxy = { return { $: 'TokenPool_SetRMNProxy', queryId: s.loadUintBig(64), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), } }, store(self: TokenPool_SetRMNProxy, b: c.Builder): void { @@ -4294,13 +4196,13 @@ export const TokenPool_RateLimitConfiguredNotification = { /** > struct (0xe5d08b2e) TokenPool_RMNProxySet { > queryId: uint64 - > rmnProxy: address + > rmnProxy: address? > } */ export interface TokenPool_RMNProxySet { readonly $: 'TokenPool_RMNProxySet' queryId: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null } export const TokenPool_RMNProxySet = { @@ -4308,7 +4210,7 @@ export const TokenPool_RMNProxySet = { create(args: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }): TokenPool_RMNProxySet { return { $: 'TokenPool_RMNProxySet', @@ -4321,7 +4223,7 @@ export const TokenPool_RMNProxySet = { return { $: 'TokenPool_RMNProxySet', queryId: s.loadUintBig(64), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), } }, store(self: TokenPool_RMNProxySet, b: c.Builder): void { @@ -4415,44 +4317,6 @@ export const TokenPool_ChainUpdatesApplied = { } } -/** - > struct (0xd7f5c563) TokenPool_RampAccessUpdatesApplied { - > queryId: uint64 - > } - */ -export interface TokenPool_RampAccessUpdatesApplied { - readonly $: 'TokenPool_RampAccessUpdatesApplied' - queryId: uint64 -} - -export const TokenPool_RampAccessUpdatesApplied = { - PREFIX: 0xd7f5c563, - - create(args: { - queryId?: uint64 - }): TokenPool_RampAccessUpdatesApplied { - return { - $: 'TokenPool_RampAccessUpdatesApplied', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_RampAccessUpdatesApplied { - loadAndCheckPrefix32(s, 0xd7f5c563, 'TokenPool_RampAccessUpdatesApplied'); - return { - $: 'TokenPool_RampAccessUpdatesApplied', - queryId: s.loadUintBig(64), - } - }, - store(self: TokenPool_RampAccessUpdatesApplied, b: c.Builder): void { - b.storeUint(0xd7f5c563, 32); - b.storeUint(self.queryId, 64); - }, - toCell(self: TokenPool_RampAccessUpdatesApplied): c.Cell { - return makeCellFrom(self, TokenPool_RampAccessUpdatesApplied.store); - } -} - /** > struct (0x28cbcc64) TokenPool_FeeConfigApplied { > queryId: uint64 @@ -4572,6 +4436,85 @@ export const BurnMintTokenPool_ClaimMinterAdmin = { } } +/** + > struct (0xba302a47) BurnMintTokenPool_BurnContext { + > wallet: address + > forwardPayload: Cell + > } + */ +export interface BurnMintTokenPool_BurnContext { + readonly $: 'BurnMintTokenPool_BurnContext' + wallet: c.Address + forwardPayload: TokenPool_LockOrBurnForwardPayload +} + +export const BurnMintTokenPool_BurnContext = { + PREFIX: 0xba302a47, + + create(args: { + wallet: c.Address + forwardPayload: TokenPool_LockOrBurnForwardPayload + }): BurnMintTokenPool_BurnContext { + return { + $: 'BurnMintTokenPool_BurnContext', + ...args + } + }, + fromSlice(s: c.Slice): BurnMintTokenPool_BurnContext { + loadAndCheckPrefix32(s, 0xba302a47, 'BurnMintTokenPool_BurnContext'); + return { + $: 'BurnMintTokenPool_BurnContext', + wallet: s.loadAddress(), + forwardPayload: loadCellRef(s, TokenPool_LockOrBurnForwardPayload.fromSlice), + } + }, + store(self: BurnMintTokenPool_BurnContext, b: c.Builder): void { + b.storeUint(0xba302a47, 32); + b.storeAddress(self.wallet); + storeCellRef(self.forwardPayload, b, TokenPool_LockOrBurnForwardPayload.store); + }, + toCell(self: BurnMintTokenPool_BurnContext): c.Cell { + return makeCellFrom(self, BurnMintTokenPool_BurnContext.store); + } +} + +/** + > struct (0xb3d52361) BurnMintTokenPool_MintContext { + > forwardPayload: Cell + > } + */ +export interface BurnMintTokenPool_MintContext { + readonly $: 'BurnMintTokenPool_MintContext' + forwardPayload: TokenPool_ReleaseOrMintForwardPayload +} + +export const BurnMintTokenPool_MintContext = { + PREFIX: 0xb3d52361, + + create(args: { + forwardPayload: TokenPool_ReleaseOrMintForwardPayload + }): BurnMintTokenPool_MintContext { + return { + $: 'BurnMintTokenPool_MintContext', + ...args + } + }, + fromSlice(s: c.Slice): BurnMintTokenPool_MintContext { + loadAndCheckPrefix32(s, 0xb3d52361, 'BurnMintTokenPool_MintContext'); + return { + $: 'BurnMintTokenPool_MintContext', + forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), + } + }, + store(self: BurnMintTokenPool_MintContext, b: c.Builder): void { + b.storeUint(0xb3d52361, 32); + storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); + }, + toCell(self: BurnMintTokenPool_MintContext): c.Cell { + return makeCellFrom(self, BurnMintTokenPool_MintContext.store); + } +} + /** > struct Storage { > poolData: Cell @@ -5376,51 +5319,6 @@ export const TokenPool_RateLimitConfigured = { } } -/** - > struct TokenPool_RampAccessUpdated { - > remoteChainSelector: uint64 - > onRamp: address? - > offRamp: address? - > } - */ -export interface TokenPool_RampAccessUpdated { - readonly $: 'TokenPool_RampAccessUpdated' - remoteChainSelector: uint64 - onRamp: c.Address | null /* = null */ - offRamp: c.Address | null /* = null */ -} - -export const TokenPool_RampAccessUpdated = { - create(args: { - remoteChainSelector: uint64 - onRamp?: c.Address | null /* = null */ - offRamp?: c.Address | null /* = null */ - }): TokenPool_RampAccessUpdated { - return { - $: 'TokenPool_RampAccessUpdated', - onRamp: null, - offRamp: null, - ...args - } - }, - fromSlice(s: c.Slice): TokenPool_RampAccessUpdated { - return { - $: 'TokenPool_RampAccessUpdated', - remoteChainSelector: s.loadUintBig(64), - onRamp: s.loadMaybeAddress(), - offRamp: s.loadMaybeAddress(), - } - }, - store(self: TokenPool_RampAccessUpdated, b: c.Builder): void { - b.storeUint(self.remoteChainSelector, 64); - b.storeAddress(self.onRamp); - b.storeAddress(self.offRamp); - }, - toCell(self: TokenPool_RampAccessUpdated): c.Cell { - return makeCellFrom(self, TokenPool_RampAccessUpdated.store); - } -} - /** > struct RateLimiter_Config { > isEnabled: bool @@ -5556,7 +5454,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class BurnMintTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('te6ccgICAQgAAQAAQbcAAAEU/wD0pBP0vPLICwABAgFiAAIAAwICygAEAAUCASAA2gDbAgEgAAYABwIBSAC2ALcCASAACAAJAgEgAHwAfQIBIAAXABgCASAACgALAgEgAAwADQIBIAASABMCASAADgAPAgEgABAAEQBpCDCTZMwcHHgcXqTIsIAjiAicbDAAZyE/yKpBCG+8oRmqAHeAqsAIMIAlFEgqALeAugwMXCAAjwwI7ORf5UgwADDAOKRMOBTUvgjJ6EQaF40EDdIgFKA8As1UWW5lYE/rfLw4FM0uY4QXwUglYE/rvLw4TCBP67y8OBQNKFQNIABZDAjs5F/lSDAAMMA4pEw4FNS+CMnoRBoXjQQN0iAUoDwCzVQRaBQBbYIREQDgADUII4VNly8lYE/rPLw4FFSqKAUtgj4I1BE4FuACASAAFAAVAgEgAD4APwD1GwzMyDQAsj6UswSzMkB1ywn0+0iJPK/1ws/IdDUMddM0PoAMfoAMIIK+vCA+CjIz5LowKkeUmD6UiXPFMnIz5LowKkeUnD6UhbMycjPkWV8HvIVyz9QA/oCEvpUE/QA9ADJyM+FiBP6UgH6As+Bc/oCcc8LZczJcfsAgAfUggDKBQnDABny9CTQ0/8x10zQyM+Q1H3fjifPCz8WzBTLHxL6VMkByMsfWPoCUAX6AskDyPpSFMwSzMkC+kgw+ChtyEAzgQEL9EEhyPpSEvpS9ADJI8jPks9UjYYUzMnIz4mIAVMkyM+E0MzM+RbPC//PhBBz+gKBAIyAAFgAwzwtrFMzMz5GiQogWyz/0AMmAQPsAgQCHAgFIABkAGgIBWAA7ADwE8T4keMC7UTQ1NTRIdDU1NMH9AT0BNGBAIdtbW1tbW2S8A0AbW1tbZLwDABtgQCIVhVWFfiS+JcDERIDAhERAgMREAMQLxA+EC0QPBArEDoQKRA4ECcQNl4iECNWGvAPVxFfDgLjAl8HItcsIcxMcmzjAtcsJqmTttyAAGwAcAB0AHgGpO2i7fvXLCeQ2+0MjkTXLCfPFPJUlFtw2zHhggDCiiNus/L0IYIAwooExwUT8vQgbQPXCz+LAgHIyz8V+lIS+lLJyM+HIBTOcc8LYRPMyXD7AOMNf4AA6BHbtRNDU1NEC1ywn////9PK/1NMHMdcKHwHQ1ywgfFP1LOMC1ywiyvg95OMC1ywgAAAArOMC1ywjRIUQLAAfACAAIQAiADw4OAfDAJE0kzUQNOICyMzMywf0ABL0AMnIzMzJ7VQA9jMC1ws/+JIi0NTUMdMHMfQEMfQEMdHQ1PpIMdQx+kgx1DHTHzH6UDH0BDHR0PpI+lAx0QGCAMKIAscF8vSCCvrwgCLQ1NQx0wcx9AQx9AQx0dDUMfpIMdQx+kgwyM+FiPpSAfoCghD7iOEZzwuKyz/JgED7AMjMzMntVAJqjhczAtM/+kj0BfiSEEVVIPAOAcjMzMntVODXLCbQIxhk4wLXLCWn8uBk4wJfA4QPAccA8vQAMwA0APoxIdDU1DHTBzH0BDH0BDHR0NQx+kgx1DH6SNdMggCvyPiS+CgkyM+EAhL6UvpSySPIz4TQzMz5FsjPigBAy//PUMcF8vQC0z/6APpI+lD0BPoA+CjIz4QC+lIY+lLJUAjIz4TQzMz5FsjPigBAy//PUAdVBfAGMMjMzMntVAH+MdM/MfoAMfpQMfQFggCvyCFus/L00NcsJdGBUjzyv/pI10yCAK/I+JJQA8cFEvL00CHQbBLU1NMH9AT0BNEF+kgx1NdMIdDXLCfT7SIk8r/TP9dM0NP/MddM0NM/MfpI+gAwghAF9eEAKdDUMfpIMdQx+kjXTPgoyM+EAvpSEgAjAf4xggCvyfiSI9DU1DHTBzH0BDH0BDHR0NQx+kgx1DH6SDDHBfL010zQ1ywgvGoozPK/0z8x+gAx+lAx+lAx+gAx9AQhbpgxIMcAkjBt4JLR0OIgbrOTyM7JkjBt4oIAr8khbrPy9NDXLCWeqRsM8r/XTNAh0GwS1NTTB/QE9ATRACkCGuMC1ywmKjtpXOMC8j8AKgArA/76UskByM+E0MzM+RbIz4oAQMv/z1BtiwTIz5A+KfqWF8s/UAT6AlJA+lIU+lQS9ADPhCATzsnIz4UIEvpSWPoCcc8LaszJc/sAIdDXLCfT7SIk8r/TPzHU1wsfAdDT/zHXTNAC0ALXCz8C+gAx+gAwAeMP0NcsJ9PtIiTyv9M/ACQAJQAmAv6CAMn4UySAQPQOb6ES8vTU9ATU1NEg0NTU0QHQ03fTP9IA03fTd9EijjdfBgHQ1NTRAdDTd9M/0gDTd9N30S/Q1DH6SDHUMfpIMFKg8AoEyMt3E8s/ygDLd8t3ycjMzMkB4w0DyMwS9ADMzFQgJYBA9EMm0NQx+kgx1DH6SDACACcAKAH6ggDJ+FMkgED0Dm+hEvL01PQE1NTRAdDU1NEB0NN30z/SANN303fRL9DUMfpIMdQx+kgwUqDwCgTIy3cTyz/KAMt3y3fJyMzMyQPIzBL0ABLMzFQgJYBA9EMm0NQx+kgx1DH6SDACyMs/EvpSUAP6AsnIz48YAASCEDDrq9sAMQB61DHTHzH0AfpQMCBukVuOGsjPhQj6UoIQNHbqcs8Ljss/z4q/IsmAQPsA4gPIzBLMywf0APQAycjMzMntVABUNi/Q1DH6SDHUMfpIMBBFEDRBMFQmoPAKBMjLdxPLP8oAy3fLd8nIzMzJAEbIyz8S+lJQA/oCycjPjxgABIIQFB9+LM8L93HPC2HMyXD7AAL2BfpIMdTXTCHQ1ywhqPu/HPK/0z8x1NcLHwHQ0/8x10zQAtAC+kgx1ws/AtMfMfoAMAHjDwTIzBPMywf0ABL0AMkB0NcsIaj7vxzyv9M/0x8x+lAwIG6RW44ayM+FCPpSghDvDLNuzwuOyz/Pir8myYBA+wDiyMzMye1UACwALQP+MdM/MfQFggCvzCFus/L00NcsJZ6pGwzyv9TR0CHQbBLU1NMH9AT0BNEF+kgx1NdMIdDXLCGo+78c8r/TPzHU1wsfAdDT/zHXTNAC0AL6SDHXCz8C0x8x+gAwAeMPBMjME8zLB/QAEvQAyQHQ1ywhqPu/HPK/0z/THzH6UDAgbgAsAC0ALgH0ItDU1NMH9AT0BNGBAIdtbW1tbW2S8A0AbW1tbZLwDABtgQCIVhcRFdM/+kjTP/oA0x/TByHCAfJF9AT6SPQFVhwRG1YcERshERshERshERsRGhEkERoRGREiERkBERgBERcRFhEkERYRFREiERUBERQBERMREhEkERIAMgL+ggDJ+FMkgED0Dm+hEvL01PQE1NTRINDU1NHQ03fTP9IA03fTd9EijjdfBgHQ1NTR0NN30z/SANN303fRL9DUMfpIMdQx+kgwUqDwCgTIy3cTyz/KAMt3y3fJAcjMzMkB4w0DyMwS9ADMzFQgJYBA9EMm0NQx+kgx1DH6SDACyAAvADAB+oIAyfhTJIBA9A5voRLy9NT0BNTU0QHQ1NTR0NN30z/SANN303fRL9DUMfpIMdQx+kgwUqDwCgTIy3cTyz/KAMt3y3fJAcjMzMkDyMwS9AASzMxUICWAQPRDJtDUMfpIMdQx+kgwAsjLPxL6UlAD+gLJyM+PGAAEghB0Ca2PADEASpFbjhrIz4UI+lKCEO8Ms27PC47LP8+KvzLJgED7AOLIzMzJ7VQAVjYv0NQx+kgx1DH6SDAQRRA0QTBUJqDwCgTIy3cTyz/KAMt3y3fJAcjMzMkARMs/EvpSUAP6AsnIz48YAASCEDR//HzPC/dxzwthzMlw+wAAGM8L93HPC2HMyXD7AACMERERIhERAREQAQ8OESQODREiDVDLChEkCgkRIglQhwYRJAYFESIFUEMCESQCAREiAREj8CoByMzMywf0APQAycjMzMntVAH4MwLTP/QF+JKCAK/KIm6z8vQh0NcsJZ6pGwzyv9TR0NQx10zQItDXLCWeqRsM8r/U0dDXTNDXLCGo+78c8r/XTNDT/zHXTND6SDAl0NTUMdMHMfQEMfQEMdHQ1DH6SDHUMfpIMPgobchARIEBC/RBIsj6UlIw+lL0AMlSgAA1Av4zAtdM+JIB0PpI+gAx9AQx+gAx0z8x0x8x1NHQ1ywjmxaE5PK/0z8x+gAx+lCCAK/LAm4S8vT0BCFumDEgxwCSMG3gktHQ4iBus5PIzsmSMG3iggCvyyFus/L0INDXLCWeqRsM8r/XTNAB0NcsJZ6pGwzyv9TR0NdM0InXJ/K/ADYANwDuggCvygLIz4TQzMz5FsjPigBAy//PUCXHBfL0AtMfMfoAMIIQBfXhACBtggiYloAIyPQAz1DIz5BeNRRmKs8LP1AF+gL6VBT6VFAG+gLOycjPhYgT+lJQBPoCz4Fz+gKAFc8LhRTLP/pSAfoCzMlx+wDIzMzJ7VQACDUfd+MB/tdM0NP/MddM0PpIMCTQ1NQx0wcx9AQx9AQx0dDUMfpIMdQx+kjXTPgobchAVYEBC/RBI8j6UhT6UhP0AMknggCvy1MhyM+E0MzM+RbIz4oAQMv/z1AYxwUX8vRQBcjPhNDMzPkWyM+KAEDL/89QyM+EAvpSFPpSyVADyM+E0MwAOAH6zPkWyM+KAEDL/89QAYIAr8sCxwXy9CHQbBLU1NMH9AT0BNEF+kgx1NdMAdDXLCGo+78c8r/TP9TTHzH6UDAB0NP/MddM0APQA/pI0z/T/zH6SDAF0x8x+gAx+gAw+CgDyPpSyQbI+lIT+lIi+gIVzMnIz48YAASCEOnADJcAOQCQzwv3cM8LYRXLPxTMyXD7ACBukl8Djh7IUAP6AsnIz4UIE/pSghDg6IL1zwuOyz/MyYBA+wDiA8jMEszLB/QA9ADJyMzMye1UAGZsEtM/+kgwggDCiFE0xwUT8vSCAMKJUyPHBbPy9CGLAsjPhyDOcM8LYRLLPxL6Uslw+wAB9RUdlRUdlQmbGH0BCFumDEgxwCSMG3gktHQ4iBulTBtbW1wjhXXLCFrCwBkl/pI9ASBAIXgMG1tbXDilCFuwwCRf+KWXwNtbW1wjhUh0NcsIAy1A7wxk4EAheBfA21tbXDibCGTXwlw4QjI+lLPkD4p+pYXyz9QBfoCE4AA9AFcIW6SW3DggmkAAAAAAAAAAAAAAAAAAAEigwb0Dm+hMZJbf+ABgwb0Dm+hMYAA6+lL6VPQAAfoCzsnIz4UIEvpScc8LbszJgED7AH8B9wyIW6SXwPgJNA1BNTU0wf0BPQE0YIAyf4l0NQx+kgx1DH6SDAYxwUX8vSCAMn++CgZxwUY8vQD0NcsJdGBUjzyv/pI10zQggCvyCTQ1DH6SDHUMfpI10z4KMjPhAL6UhL6UskByM+E0MzM+RbIz4oAQMv/z1ATxwUS8vSAAQAPTO2i7fsg1ywn0+0iJJ5sIdM/1NMf9AT6UDDwEI/K1ywgR5f9vI8/1ywlNv0xHI601ywjmxaE5I4SMdM/+gD6UFUD8YAYgB2AFts4jpbXLCGo+78cnGwh0z/U0x/6UDDwHOMO4uMN4w3if4ABCAEMARAHw+kjU10wB0AHQAdcsJ9PtIiTyv9M/1NMfMfQB+lAwAdDT/zHU0dDUMdM/+kgx+gAx+kjRBPoAMfoA1NdMBsj6Uhf6UiH6AsnIz48YAASCEDfdb27PC/dwzwthE8s/EszJcPsAIW6SXwXjDgHIzMzLBxP0ABL0AMkBAEEAQgTIzBPMycjPhYgT+lKCEPQypOPPC47LP8wB+gLJgED7AAP21ywk8VNbNI4zbDHTPzHXTND6SNTU0QHQAdAB1ywhqPu/HPK/0z/U0x/6UDAE0x/6APoAMBBWEEUQNPAjj7zXLCEPOOw8jzHXLCYqO2lcjhxsIdM/+kjTP/oA0x/TByHCAfJF9AT6SPQFbfAkjwnXLCaRSias4w/i4w3iAEUARgBHAt5sMdM/MddM0PpIMdTU0SHQ1ywn0+0iJPK/0z8x1NcLHwHQ0/8x10zQAtAC1ws/AvoAMfoAMAHjD9DXLCfT7SIk8r/TP9Qx0x8x9AH6UDAgbpFbjhrIz4UI+lKCEDR26nLPC47LP8+LKBbJgED7AOIAdwB4AOhsMdM/MddM0PpI1NTRAdAB0AHXLCfT7SIk8r/TP9TTH/QE+lAwBfoA+gDU10zIz5Pp9pESKM8LPxfMFcsfE/QAUmD6VMnIWPoCWPoCEswSzMnIz5OejUEGE8s/FPpSE8wSzMnIz4WIEvpScc8LbszJgED7AAH+bCHTP/pI0z/6ANMf0wchwgHyRfQE9AVWHtDUMfpIMdQx+kgx1DHTH/pQMfQEMdFSQPAWggDJ/VYf0NQx+kgx1DH6SDAoxwXy9IIAyfkmVh2AQPQOb6Ex8vRWHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHgBIAyjXLCGDCVi8jwnXLCK3uem84w/jDQBJAEoASwLcbDHTPzHXTND6SDHU1NEh0NcsIaj7vxzyv9M/MdTXCx8B0NP/MddM0ALQAvpIMdcLPwLTHzH6ADAB4w/Q1ywhqPu/HPK/0z/THzH6UDAgbpFbjhrIz4UI+lKCEO8Ms27PC47LP8+LKBbJgED7AOIAcgBzAGBWHlYeVh5WG1YbVhtWG/AmCMjLHxfLH1AF+gJQA/oCyw/LD8oAAfoCygDJ+ChZ8CQD/mwh0z/U10xWGdDU+kgx1DH6SDHUMdMfMfpQMfQEMdHQ+kj6UDHRJIIAwogCxwXy9AHQlCDHALOOQCDXSwGRMJuBNLwBwAHy9NdM0OLTP1IQERiAQPRbggDJ+AHy9MjPjxgABIIQJ5CCi88L93DPC2ESyz/JcPsAERboMNCKiugATABNAE4DKNcsIL4SFuSPCdcsIhNcZiTjD+MNAFIAUwBUA/5sIdM/bwAB0wf0BJMhbrOOEgHQ9ASa+khQVW+MJMcAFeYwAegxIm+IWLryifQFVhnQ1DH6SDHUMfpIMdQx0x8x+lD0BDHRggDJ/iFus5UFxwXDAJMxNHDiFPL0ggDKBiNus/L0AtD6SPQE9AUgbuMPyM+FCBL6UnHPC27MyYBAAG8AcABxAAggxwCzAf4g10sBkTCbgTS8AcAB8vTXTNDi0z/U1NSCAMn3I9DTByHBQfKFAaoC1xjR10nDAPL0ggDJ+yVWG4BA9A5voTGz8vQB0NTU0W0C0NIA03fTd9H4IyLIy3fLPxPKAMt3y3fJAdDSANN303fR+CMiyMt3yz8TygDLd8t3yQHIzMzJAE8AMDDIz4UIEvpSghCteDPXzwuOyz/JgED7AAG4+CNwyMt3yz9wzwvwyfgjcMjLd8s/cM8L8MkByMzMySQG0JQgxwCziugwBcjMEvQAzBPMUjIRGYBA9EPIz48YAASCEO03xLzPC/dwzwthE8s/AREXAczJcPsAERUAUAH6INdLAZEwm4E0vAHAAfL010zQ4tMHIcFB8oUBqgLXGMgi10kgqTgC8kWrAiDBQfKFzwsHEs7JggDJ9yHQ0wchwUHyhQGqAtcY0ddJwwDy9CD5AIIAyf9TFoMH9A5voTGz8vRUQRaDB/QXyM+PGAAEghC/DRq2zwv3cM8LYSkAUQASzws/FczJcPsAAfxsIdM/0z/XTFYZ0NT6SDHUMfpIMdQx0x8x+lAx9AQx0dD6SPpQMdEkggDCiALHBfL0ggDJ+CJWGIBA9A5voTHy9IIAyfgiVhiAQPQOb6ES8vTU9ATU1NEk+QBQA4MH9FuCAMoAAfL0A8jME/QAEszMUiIRGIBA9EPIz48YAAQAVQLq1ywmu4lAhI7q1ywh4oUc3I5fbCHTP9cLHxEY0NT6SNT6SNTTHzH6UPQE0SbQ+kj6UDHRKYIAwogCxwXy9FYeB8jMFvpSFMwS+lLME8sfEvpU9ADJyM+FCBP6UoIQQmpxO88Ljss/AREXAcsfyYBA+wDjDuMNAFYAVwH+bCHTP9M/10xWGdDU+kgx1DH6SDHUMdMfMfpQMfQEMdHQ+kj6UDHRJIIAwogCxwXy9IIAyfgiVhiAQPQOb6Ex8vSCAMn4IlYYgED0Dm+hEvL01PQE1NTRggDJ9yXQ0wchwUHyhQGqAtcY0ddJwwDy9CT5AIIAyf9TFIMH9A5voQBuAHCCELwUx+jPC/dwzwthIs8LP1YXzxTJcPsAyM+FCBT6UoIQ4XvzzM8LjhLLP8s/AREUAczJgED7AAP41ywh+uT6vI9x1ywhw0cYTI5cbCHTP/QFERjQ1PpI1PpI1NMf+lD0BDHRJtD6SPpQMdEpggDCiALHBfL0Vh4HyMwW+lIUzBL6UszLH/pU9ADJyM+FCBP6UoIQidYC5c8Ljss/AREXAfQAyYBA+wCPCdcsJCHCChTjD+LjDQBYAFkAWgL+bCHTP/pI+lD6UDARGtDU+kjUMfpI1NMf+lD0BNEm0PpI+lAx0SuCAMKIAscF8vSCAMn3KY0IYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMcFs/L0ggDJ91YhbpF/4w7y9G0pyPpSUpD6VFYiAfpU9ADJB8jMFgBsAG0A9Gwh0z/0BREY0NT6SNT6SNTTH/pQ9ATRJ9D6SPpQMdEqggDCiALHBfL0BdD6SPpQ+lD0BDHRAsj6UvpU+lQBER8B9ADJBsjMFfpSFcz6UhPMyx8BERkB+lQBERgB9ADJyM+FCBL6UoIQwf/jps8LjgERFwHLP8mAQPsABP7XLCJ/FpNkjutsIdM/10xWGNDU+kgx1PpIMdQx0x8x+lAx9AQx0dD6SPpQ+lD0BNEE0PpI+lAx0ZLwJwBUJYDsXySCAMn+A8cFkjB/lNoBwwDi8vTQlCDHALOK6DDIz4UIEvpSghDdewxxzwuOyz/JgED7AI8J1ywhhQ6PvOMPAFsAXABdAF4AvGwh0z/6UDARGNDU+kjU+kjU0x/6UDH0BNEm0PpI+lAx0SmCAMKIAscF8vRWHgfIzBb6UhTMEvpSzMsfEvpU9ADJyM+FCBP6UoIQPIadgM8Ljss/AREXAfpUyYBA+wAC/iDXSwGRMJuBNLwBwAHy9NdM0OLTP9IA1NSCAMn4JVYbgED0Dm+hEvL01PQE1NTRJ44/AdDUMdQx0SXQ0gDTd9N30fgjIsjLd8s/E8oAy3fLd8kl0NIA03fTd9H4IyLIy3fLPxPKAMt3y3fJAcjMzMkB4w0DyMwS9ADMzFJSERsAXwBgAv5sIdM/1NdMVhnQ1PpIMdQx+kgx1DHTHzH6UDH0BDHR0PpI+lAx0SSCAMKIAscF8vQB0JQgxwCziugw0JQgxwCzjjog10sBkTCbgTS8AcAB8vTXTNDi0z9SEBEWgED0WzDIz48YAASCENZGx9HPC/dwzwthEss/yXD7ABEU6DDIAGEAYgMo1ywnGDsl9I8J1ywkyU2yFOMP4w0AZQBmAGcAAuIAetDUMdQx0SXQ0gDTd9N30fgjIsjLd8s/E8oAy3fLd8kl0NIA03fTd9H4IyLIy3fLPxPKAMt3y3fJAcjMzMkASoBA9EPIz48YAASCEP+dv3bPC/dwzwthFcs/E8oAzMzJcPsAERUB/iDXSwGRMJuBNLwBwAHy9NdM0OLTP9Mf0x/6APoA0w/TD9IAggDJ+ClWIIBA9A5voTHy9IIAyfUi8vSCAMn0JIEnELny9IIAyfQjgScQufL0ggDJ9SjCAPL0J8jLHyfPCx8m+gIl+gIkzwsPI88LDyLPCgBSkhEfgED0QwfIyx8AYwEsic8WEvpSghAoy8xkzwuOyz/JgED7AABkAF4Wyx9QBPoCWPoCyw/LD8oAycjPjxgABIIQ++YfFc8L93DPC2ETyz8SzMlw+wARFQABQgH+bCHTP/pIMBEY0NT6SDHU+kjU0x/6UPQE0SbQ+kj6UDHRKYIAwogCxwXy9IIAyfdWH40IYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMcFs/L0Vh4HyMwX+lIVzBP6UszLH/pU9ADJyM+FCBP6UoIQ5dCLLs8LjgBoAfDXLCTtJtBMjm1sIdM/9AVWGNDU+kjUMfpIMdQx0x8x+lAx9AQx0QHQ+kj6UDHRJIIAyf4CxwWSMX+WUkLHBcMA4vL0ERfQ9AT0BPQEMdFWGALI9AD0APQAycjPhQgT+lKCEBWAAWHPC47LPwERFgH0AMmAQPsA4w4AaQHYbCHTP9dMVhjQ1PpIMdQx+kgx1DHTHzH6UDH0BDHR0PpI+lAx0SOCAMKIAscF8vQRF9D0BPQE9ATRERnQlCDHALOK6DAByPQA9AABERcB9ADJyM+FCBL6UoIQ1/XFY88LjgERFgHLP8mAQPsAAGsAGss/AREXAfpSyYBA+wAB7tcsIGgEyuSOVDAxVhfQ1PpI1PpI1NMf+lD0BNEH0PpI+lDRQQop8AGOKjhXHhEdyPpSFvpUycjME/pSzPpSEswBERgByx/6VAERFgH0AMkRFX/bMeAQiV8JxwDbMeEx0z9vAAHTB/QFkyBus4roMCFviLryifAoAGoAWtD0BI4m+kj6ANcsIHxT9Szyv9M/+gD6SPpQ9AT6AIsIDFWAbwlvjCPHABTmMADUINdLAZEwm4E0vAHAAfL010zQ4tM/+lD6UCJul1I2gED0WzCbIsj6UlQgR4BA9EPiIW6XUjWAQPRbMJshyPpSVCBGgED0Q+IDyMs/EvpU+lTJyM+PGAAEghCcWruVzwv3cc8LYczJcPsAWABWViGNCGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATHBbPDAABu+lIWzBP6UszLHxL6VPQAycjPktzXjDIUyz8S+lL6VAERGAH6VMnIz4UIEvpScc8LbszJgED7AACyMbPy9FRFFIMH9BfIz48YAASCEL8NGrbPC/dwzwthJs8LPyXPFMlw+wADyMwT9AASzMxSIhEYgED0Q8jPhQgU+lKCEBLMSYXPC44Syz/LPwERFAHMyYBA+wAAfjDIz5Gxwst2E8s/I2+Ic21UciGpBo4bAcj0AFMhtghRIqEimVOAb4FY+lIBpOQByQKh5DAxNc8LBxP0APQAyQCCyM+QVjdfVhTLPyRviHNtVHIhqQaOGwHI9ABTIbYIUSKhIplTkG+BWPpSAaTkAckCoeQwMTbPCwcU9AASzBL0AMkABPsAAvyCAMn4IlYXgED0Dm+hEvL01PQE1NTRINDU1NHQ03fTP9IA03fTd9EijjhfBgHQ1NTR0NN30z/SANN303fRViHQ1DH6SDHUMfpIMFKg8AoEyMt3E8s/ygDLd8t3yQHIzMzJAeMNA8jMEvQAzMxSIhEXgED0Q1YY0NQx+kgx1DEAdAB1AfiCAMn4IlYXgED0Dm+hEvL01PQE1NTRAdDU1NHQ03fTP9IA03fTd9FWIdDUMfpIMdQx+kgwUqDwCgTIy3cTyz/KAMt3y3fJAcjMzMkDyMwS9AASzMxSIhEXgED0Q1YY0NQx+kgx1DH6SDACyMs/EvpSAREV+gLJyM+PGAAEAHYAWDZWIdDUMfpIMdQx+kgwEEUQNEEwVCag8AoEyMt3E8s/ygDLd8t3yQHIzMzJAFD6SDACyMs/EvpSAREV+gLJyM+PGAAEghA0f/x8zwv3cc8LYczJcPsAACSCEHQJrY/PC/dxzwthzMlw+wAC/oIAyfgiVheAQPQOb6ES8vTU9ATU1NEg0NTU0QHQ03fTP9IA03fTd9EijjhfBgHQ1NTRAdDTd9M/0gDTd9N30VYh0NQx+kgx1DH6SDBSoPAKBMjLdxPLP8oAy3fLd8nIzMzJAeMNA8jMEvQAzMxSIhEXgED0Q1YY0NQx+kgx1DEAeQB6AfiCAMn4IlYXgED0Dm+hEvL01PQE1NTRAdDU1NEB0NN30z/SANN303fRViHQ1DH6SDHUMfpIMFKg8AoEyMt3E8s/ygDLd8t3ycjMzMkDyMwS9AASzMxSIhEXgED0Q1YY0NQx+kgx1DH6SDACyMs/EvpSAREV+gLJyM+PGAAEAHsAVjZWIdDUMfpIMdQx+kgwEEUQNEEwVCag8AoEyMt3E8s/ygDLd8t3ycjMzMkAUPpIMALIyz8S+lIBERX6AsnIz48YAASCEBQffizPC/dxzwthzMlw+wAAJIIQMOur288L93HPC2HMyXD7AAIBIAB+AH8CASAAlwCYAgEgAIAAgQIBIACQAJECASAAggCDAgEgAI4AjwH1CbDAJUsbrPDAJFw4o4XVxQHERUHBhEUBlUEERMr2oJQ/oEAhw7gERURGxEVERQRGhEUERMRGRETERIRGBESERERFxERERARFhEQDxEbDw4RGg4NERkNDBEYDAsRFwsKERYKCREbCQgRGggHERkHBhEYBgURFwUEERYEgAIQB9wmwwCVK26zwwCRcOKOIFcUBxEVBwYRFAZVBBETKtqHBhEUBgUREwWBAIcRE1VA4CPQ0//U0SDQ0z/6SDH6APpIMIIAyf1WIdDUMfpIMdQx+kgwWMcF8vSCAMn5IlYegED0Dm+hMfL0Vh9WH1YfVh9WH1YfVh9WH1YfVh+AAhgH+AxEbAwIRGgIBERkBERhWF1YXVh1WHVYdVh3wEQSOKF8EVxZXFlcWVxZXFlcWDxEVDw4RFA4NERMNDBESDAsREQsKERAKVVngyM+T6faRElYbzws/AREgAcwBER4Byx8BERwB9ABWGgH6VMnIAREc+gIBERz6AgERHAHMAREaAQCFAMjMycjPk56NQQYBERYByz8BERYB+lIBERcBzAEREwHMycjPhYgBERUB+lJxzwtuAREUAczJgED7AA8RFQ8OERQODRETDQwREgwLERELChEQChCfEI4QfRBsEFsQShA5SBZFVQcDAfxWH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WF/ASVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YY8BNWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwJWHwIAhwP+Vh8CVh8CVh8CVh8CVh8CVh8CAREZAREYVhzwFGahJeMPggDJ+FExgED0Dm+hFPL0AtT0BDHUMdQx0VYcyMv/ycjPk+n2kRIpzws/KM8UJ88LH1Jg9ABSUPpUycgk+gJWHvoCI88UIs8UyStWIglWIglWIlGQCVYiCVYiCVYiCQCIAIkAigL+Vh7Q1DH6SDHUMfpIMdQx0x/6UDH0BDHRUmDwFoIAyfgjVh2AQPQOb6ES8vTU9ATU1NHQ1NTRAdDTd9M/0gDTd9N30SKOM1Yn0NQx+kgx1DH6SDBSoPAJBMjLdxPLP8oAy3fLd8nIzMzJA8jMEvQAzMxSMhEdgED0Q+MOVh7Q1ACLAIwB9IIAyfgjVh2AQPQOb6ES8vTU9ATU1NEB0NTU0QHQ03fTP9IA03fTd9FWJ9DUMfpIMdQx+kgwUqDwCQTIy3cTyz/KAMt3y3fJyMzMyQPIzBL0ABLMzFIyER2AQPRDVh7Q1DH6SDHUMfpIMCPIyz/6UlYc+gLJyM+PGAAEAI0AoFYiCVYiCVYiCVYiCVYiCVYiCVYiCVYiCVYiCVYiCVYiCVYiCVYiCVYiCVYiCQgRIggHESEHBhEgBgURHwUQNBAjAREgAREf8BcCERcCUEQDAfhfCYIAyfgjVh2AQPQOb6ES8vTU9ATU1NEB0NTU0QHQ03fTP9IA03fTd9FWJ9DUMfpIMdQx+kgwUqDwCQTIy3cTyz/KAMt3y3fJyMzMyQPIzBL0ABLMzFIyER2AQPRDVh7Q1DH6SDHUMfpIMCPIyz/6UlYc+gLJyM+PGAAEAI0AWDH6SDHUMfpIMCPIyz/6UlYc+gLJyM+PGAAEghDrpIwLzwv3cc8LYczJcPsAACSCEM9QWfzPC/dxzwthzMlw+wAAbxQ7V8MNjY2NwTDAJUhbrPDAJFw4pc0EDQQI9pA4BAjXwMyggDJ+gHQ9AQx9AQx9ATRWPAHs/L0gAJEbMM3Nzc4BcMAlSZus8MAkXDilDUF2lDgWzIzAdD0BPQEMfQEMdGAQPQOb6GT+kjRkjBt4oIAyf4hbrOVAscFwwCTbCFw4vL0gAgEgAJIAkwIBIACUAJUAexXElcQXw8ybDMz0NM/+kgx+gAwAoBA9A5vofAVMWwzMwKTXwRw4QORMpEw4oIAyfQigScQufL0qIEnEKkEgAFcmTBtbW1tbW1tcOHTH9Mf+gD6ANMP0w/SANEgk4EAippfB21tbW1tbW1w4oAArCGRW+GCAMoJIZQCusMAk2whcOLy9IAGpDg5OTk6Ojo6Ojo6Ojo9PT09AsMAlShus8MAkXDijhM8EGsQWhBJCBBXEEYFEDRBMNqx4GxENAfQ1DH6SDHUMfpIMdQx0x8x+lD0BDHRIG7jA18IcIACWAJYj0PoAMfoA1DHUMdH4KATI+lITzBTMycjPkQSnRCYYyz8WzBTLHxL0AFAD+gL6UhL0AMnIz4WIEvpSz4QQc/oCcc8LZczJgED7AH8CASAAmQCaAgEgAKUApgIBIACbAJwCASAAogCjAfUJsMAlSlus8MAkXDijhgwVxMGERQGBRETBVUDERIn2nJQ/oEAhw7gggDJ/lYc0NQx+kgx1DH6SNdM+CjIz4QC+lIS+lLJAcjPhNDMzPkWyM+KAEDL/89QJ8cF8vRWG1YbVhtWG1YbVhtWG1YbVhtWG1YbVhtWG1YbVhuAAnQGTO2i7ftXElcQXw9sQiLQ1DH6SDHU+kgx1DHTHzH6UDH0BDHR0PpI+lAx+lAx9ATRUyHHBZJfBeAggCD0hm+lMpEBiuiCAMn+8vCAAoQFYVhtWG1YbVhtWG1YbVhtWG1YbVhtWG1Ybk1vwG+3juoAbf+0Riu1B7fEB8v8AngL6IfQEIW6YMSDHAJIwbeCS0dDi+kjU1NFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgERHlYciu3juoAdf+0Riu1B7fEB8v8AnwCgAKgxEEcQNkV28Bv4J28QUwG8kaKSW3Di+C+gcvsCAdDXLCfT7SIk8r/TP9Qx0x8x9AH6UDAgbpJfA44ZyM+FCPpSghA0dupyzwuOyz/LD8mBAIL7AOIA8CDQ1ywn0+0iJPK/10zQ0/8x10zQ0z8x+kj6ADCCAMoIURi68vSCAMoHJm6z8vRWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWHgFWGwHwGVjwGgCgUwXQ1DH6SDHUMfpIMdQx0x8x+lAx9ATRggDJ/iFus/L0JMj6UhLLH1Jg+lLJAcjPhNDMzPkWyM+KAEDL/89QJMcFlF8G2zHgIYAg9HxvpTIB8wowwCVKm6zwwCRcOKOGFcWCREXCQgRFghVBhEVVhfaolD+gQCHDuBsMzMzAtAB0AHXLCfT7SIk8r/TP9TTHzH0AfpQMAHQ0/8x1NHQ1DHTP/pIMfoAMfpI0QT6ADH6ANTXTAbI+lIX+lIh+gLJyM+PGAAEghA33W9ugAKQAgwwIG6SXwTgghAF9eEAbYsEyM+QPin6lhbLP1AE+gJSIPpSEvpUEvQAz4QgEs7JyM+FCBP6UgH6AnHPC2rMyXP7AIAByzwv3cM8LYRPLPxLMyXD7ACFukl8FjiEEyMwTzMnIz4WIE/pSghD0MqTjzwuOyz/MAfoCyYBA+wDiAgEgAKcAqAIBIAC0ALUB8QlwwCVL26zwwCRcOKOGTRXEgUREwUEERIEWgEREQEt2mJQ/oEAhw7gERURGhEVERQRGREUERMRGBETERIRFxESERERFhERERARGhEQDxEZDw4RGA4NERcNDBEWDAsRGgsKERkKCREYCQgRFwgHERYHBhEaBgURGQWAAqQH1CXDAJUubrPDAJFw4o4gVxMGERQGBRETBVUDERIt2nYFERMFBBESBIEAhxESVTDgItDT/zHU1PQE9AQx0QLQ+kgx0z/UMdP/+kjRggDJ/VYg0NQx+kgx1DH6SDBYxwXy9IIAyfkiVh2AQPQOb6Ex8vRWHlYeVh5WHlYegAKsB+AQRGAQDERcDAhEWAgERGgERGVYYVhhWGFYdVh3wHQOOMF8DVxZXFlcWVxZXFhEQERUREA8RFA8OERMODRESDQwREQwLERALEK8QnhCNEHxVRuARGBEdERgRFxEcERcRFhEbERYRFREaERURFBEZERQRExEYERMREhEXERIAqgBmERERFhERERARFREQDxEUDw4REw4NERINDBERDAsREAsQrxCeEI0QfBBrEFoQSRA4WPAjAfxWHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVhfwElYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WGPAeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh4ArAH+Vh5WHlYeVheCAMoAERrwHxPy9FYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAVYdAREY8CCS8CEAElYeAlYeAlYeAlYeAlYeAlYeAlYeAlYeAlYeAlYeAlYeAgCtA/xWHgJWHgJWHgJWHgJWHgJWHgJWHgJWHgJWHgJWHgJWHgKAGH/tEdoBIMEAkX+WIIR3vMMA4paCAMoC8vDgI+MPIlYZyM+Q1H3fjifPCz8mzxQlzwsfUkD6VMkiyMsfIvoCVhz6AskpViAIViAIViBRgAhWIAhWIAhWIAhWIAgArgCvALAC/lYc0NQx+kgx1DH6SDHUMdMf+lAx9AQx0VJA8BaCAMn4IlYbgED0Dm+hEvL01PQE1NTR0NTU0dDTd9M/0gDTd9N30SKONFYl0NQx+kgx1DH6SDBSoPAJBMjLdxPLP8oAy3fLd8kByMzMyQPIzBL0AMzMUiIRG4BA9EPjDlYc0NQAsQCyAfaCAMn4IlYbgED0Dm+hEvL01PQE1NTRAdDU1NHQ03fTP9IA03fTd9FWJdDUMfpIMdQx+kgwUqDwCQTIy3cTyz/KAMt3y3fJAcjMzMkDyMwS9AASzMxSIhEbgED0Q1Yc0NQx+kgx1DH6SDACyMs/EvpSVhn6AsnIz48YAAQAswCaViAIViAIViAIViAIViAIViAIViAIViAIViAIViAIViAIViAIViAIViAIBxEgBwYRHwYFER4FBBEeBAMRHgMCER8CAREg8CIBERYBUDMB+F8JggDJ+CJWG4BA9A5voRLy9NT0BNTU0QHQ1NTR0NN30z/SANN303fRViXQ1DH6SDHUMfpIMFKg8AkEyMt3E8s/ygDLd8t3yQHIzMzJA8jMEvQAEszMUiIRG4BA9ENWHNDUMfpIMdQx+kgwIsjLP/pSVhr6AsnIz48YAAQAswBaMfpIMdQx+kgwAsjLPxL6UlYZ+gLJyM+PGAAEghDLxA5bzwv3cc8LYczJcPsAACSCEIuyX6jPC/dxzwthzMlw+wAAmRskzMzNDc3NzgHwwCVJG6zwwCRcOKVNQUD2lDgWzIzAdD0BDH0BPQEMdGAQPQOb6GT+kjRkjBt4oIAyf4hbrOVAscFwwCTbCFw4vL0gAFEVxJXEF8PMmwzAoBA9A5voZJbcOHUMfQE1DHUMdEB+QABgwf0Dm+hMYAIBIAC4ALkCASAAzgDPAgEgALoAuwIBIADCAMMCASAAvAC9AgEgAL4AvwBnFcQXw81XwMyIG6RMOAx0IIAygEh10mDB7qXIddKwADDAJFw4vL00//RggDKASGEB7vy9IACHFcQXw81XwMyUyG6kmwh4FMhvJ4CovAIloIAygLy8OCpBOACofAIloIAygLy8OCCAMoCIZmE/yKpBCO+wwCRf+Ly9KiABmQ3ODg4ODg4ODk5OTk5PDw8PMMAlSpus8MAkXDinDsQWhBJEDhFMwTaoeAybDM2BtDUMfpIMdQx+kgx1DHTHzH6UPQEMdEgbuMDXwdwgAMAB8QowwCVL26zwwCRcOKOFQoRGAoJERcJCBEWCC/aswIREAJQ/uAyMwPQ0/8x1NQx9AQx9AQx0dD6SNM/1DHT/zH6SNECyPpSyQLI+lIX+lJQA/oCEszJyM+PGAAEghDpwAyXzwv3cM8LYRXLPxTMyXD7ACJukl8D4w6AAwQCQJNDTHzH6APoAMdH4KAfI+lIYzBXMycjPkcDwrWIUyz8SzFAF+gIUyx8S+lIS9ADJyM+FiBL6Us+EEHP6AnHPC2XMyYBA+wB/ADrIAfoCycjPhYgT+lKCEODogvXPC47LP8zJgED7AAIBIADEAMUCASAAzADNA/UMlYf0NQx+kgx1DH6SDHUMdMf+lAx9AQx0VJQ8BaCAMn9ViDQ1DH6SDHUMfpIMCnHBfL0ggDJ+SdWHoBA9A5voTHy9ClWINDUMfpIMdQx+kgx1DHTHzH6UPQEMdEgbuMDW2xibwAibuMPyM+FCBL6UnHPC27MyYBA+wCAAxgDHAMgB9whljDwJhhfCOE0W1YXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAVYXAREX8CCS8CEAAREYAQERFwEBERYBAREVAQERFAEBERMBARESAQEREQEBERABHx6AAywL+O1YgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYgAVYdAREdVhxWHFYc8CX4KAfI+lIS9AAS9ADJyM+TFR20rhnLPxf6UhXLP1AF+gLLHxPLB/QA+lL0AMnIiQDJAMoAfDLIz5Gxwst2E8s/IW+Ic21UciGpBo4bAcj0AFMhtghRIqEimVNgb4FY+lIBpOQByQKh5DAxM88LB/QA9ADJAH7Iz5BWN19WFMs/I2+Ic21UciGpBo4bAcj0AFMhtghRIqEimVOAb4FY+lIBpOQByQKh5DAxNc8LBxP0AMz0AMkAAWIAKs8WEvpSz4QQc/oCcc8LZczJgED7AABQHRwbGhkYFxYVFEMwgBh/7RHaASDBAJF/liCEd7zDAOKWggDKAvLw4ACvGzEbEQ0NTU1NROAQPQOb6HwFY4WEHlfCXBUcABTABBWEEUQNEMAcFADcOEnCpI3N44fOVRBGJI3Jt6CAMn0KIEnELny9FJoqIEnEKkEFqEHBeIQR1Ujf4AAdDRbIG6zlMcFwwCSW3DigAgEgANAA0QCz0Jr4GriSuIL4eZNhmB6GoY/SQY6hj9JBjqGOmP/SgY+gIY6KkYeAsAwCB6BzfQyy24KjgAOHDpj+mP/QB9AGmH6YfpAGjLr4O4KjgAOHCDShgYgb/wGJoBv8AgEgANIA0wIBIADXANgB6QkwwCVJW6zwwCRcOKOGlcSBRETBQQREgSBAIcEERJWE9pzAhEQAlD+4GwhggDKBFYY0NTXTNAB0AH6SPpQ+lD0BZLwKQBSgOxfAfpIMCTHBZIwf5TaAcMA4vL0bYIImJaA+C+ggBT7Am1wI2+Ikly5iuhfBoADUAB8MWwiIW6zlMcFwwCSW3DigAv4hpFJTb4FvKYIA2ZQljQhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExwWz8vQsbrOOKVNMjiHtou37cCFviJJcuY4QIaRSM2+BJMcFlV8Ef9sx4OhfBHDYs8MAkXDiloIA2ZXy8OBwU5yBAQv0Cm+hkTDjDSagANUA1gAIMfoA0QD8yAH6AlQgnYEBC/RByM+QBlqB3skvyM+QtYWAMvpS9AAczsnI9ADPUCXIz5A+KfqWGMs/UAf6AhT6UhL6VPQAAfoCEs7JyM+FiFJA+lJQA/oCz4Fz+gJxzwtlEszJgBD7AMjPjxgABIIQqodQKc8L93DPC2ES+lIB+gLJcPsAAGcUKtfCWzzbDMzMyJukl8D4ALQ+kj0BcjPhQgS+lKCEARJ1GfPC44Syz8Syh/0AMmAQPsAgAfcVxJXEF8PMjMzggDJ+DQSgED0Dm+hE/L0AdQx9AQx1NTRAo5XMNDU1NEB0NN30z/SANN303fR+CNQBKEjqBSgI7YI+CMByMt3yz/KABLLd8t3yQHQ03fTP9IA03fTd9H4I1AEoSOoFKAjtgj4IwHIy3fLP8oAEst3y3fJgANkAsOAx0NTU0QHQ03fTP9IA03fTd9H4I1AEoSOoFKAjtgj4IwHIy3fLP8oAEst3y3fJAdDTd9M/0gDTd9N30fgjUAShI6gUoCO2CPgjAcjLd8s/ygASy3fLd8kCASAA3ADdAgEgAP4A/wIBIADeAN8CASAA8ADxAgEgAOAA4QIBIADsAO0CASAA4gDjAgEgAOgA6QIBZgDkAOUCAUgA5gDnAFGgX7UTQ1NQx0SDQMdTUMdMHMfQEMfQEMdHQ1DH6SDHUMfpIMdQx1wsfgBpoce1E0NTUMdEg0DHUMdQx0wcx9AT0BDHRbSGAQPSGb6UykQGdUgJvAlESgED0fG+lMugwMYAXaV12omhqahjo6GoY6mmDmPoCGPoCGOjoegIY+gJ6AhjowCB6BzfQyf0kaMkYNvFAEOnI9qJoamoY6Ohqahjpg5j6Ahj6Ahjo6GoY/SQY6hj9JBhAgEgAOoA6wDRrdh2omhqamiQ6GpqaYP6AnoCaMCAQ7a2tra2tsl4BoA2tra2yXgGAAiIiIsIiIiICIqIiAeIigeHCImHBoiJBocIiIcGiIgGiGeIXwhWiE4IRYg9CDSILAgjiBsioDaoIcCARCCZ+ApAAGypX40KmxpbmsuY2hhaW4udG9uLmNjaXAucG9vbC5CdXJuTWludFRva2VuUG9vbIItTAuMS4wgAxqrQ7UTQ1NTRIdDU1NMH9AT0BNGBAIdtbW1tbW2S8A0AbW1tbZLwDAAREREYEREREBEXERAPERYPDhEVDg0RFA0MEREMCxEQCxCvEJ4QjRB8EGsQWhBJEDhHYG1QZYEAiAXwJQIBbgDuAO8AybAfO1E0NTU0SHQ1NTTB/QE9ATRgQCHbW1tbW1tkvANAG1tbW2S8AwAERERFRERERARFBEQDxETDw4REg4NERENDxEQDxDvEM4QvRCsEJsQihB5EGgQVxBGEDVEMG1agQCIAvAfgAC2nUdqJoamoY6OhqGOoY6YP6Ahj6AhjowAZpFEEAV+RQEEIH3flCQIBIADyAPMCASAA/AD9AgEgAPQA9QBfsnG7UTQ1NQx0SDQMdTUMdMHMfQEMfQEMdHQ1DH6SDHUMfpIMdQx0x8x+lD0BDHRgAgEgAPYA9wIBYgD6APsA5qqH7UTQ1NTRIdDU1NMH9AT0BNGBAIdtbW1tbW2S8A0AbW1tbZLwDAAREREZEREREBEYERAPERcPDhEWDg0RFQ0RExEUERMREhETERIMERIMCxERCwoREAoQnxCOEH0QbBBbEEoQOUhwbVB2gQCIBlUx8CwCASAA+AD5AGemO9qJoamoY6Ohqahjpg5j6Ahj6Ahjo6Gp9JBjqGP0kGOoY6Y+Y/SgY+gIY6Oh9JH0oGOjAAmlCwIDhQBLofu1E0NTUMdEg0DHU1DHTBzH0BDH0BDHR0NQx+kgx1DH6SDDHBYAT6B/tRNDU1DHRINAx1NQx0wcx9AQx9AQx0dDUMddM0PpI+lD6UPQE0YAybJeu1E0NTU0SHQ1NTTB/QE9ATRgQCHbW1tbW1tkvANAG1tbW2S8AwAERERFRERERARFBEQDxETDw4REg4NERENDxEQDxDvEM4QvRCsEJsQihB5EGgQVxBGEDVEMG1agQCIAvArgAF+y4btRNDU1DHR0NQx1NMHMfQEMfQEMdHQ9AT0BDH0BDHRgED0Dm+hk/pI0ZIwbeKACASABAAEBAgEgAQIBAwBbtH29qJoamoY6Ohqahjpg5j6Ahj6Ahjo6GoY/SRqGP0kGOoY6Y+Y/SgY+gIY6MAB9t9z9qJoamoY6JBoGOoY6hjpg5j6Ahj6AmjAIHoHN9DHCWmP6Y/9AH0AaYfph+kAaMCARUyYNra2tra2trhxQAJu0jb2omhqahjokGgY6hjqGOmDmPoCegIY6MEAZPwswCB6BzfQiXl6ahj6AmoY6hjotpDBg/pDN9LITwDqaKw3gSiJQYP6PjfS9AgRr4HACASABBAEFAgFqAQYBBwA9shx7UTQ1NQx0dDUMdQx0wcx9AT0BDHRgED0Dm+hMYABfpV/aiaGpqGOiQaBjqGOoY6YOY+gJ6AhjowQBk/CzAIHoHN9CJeXpqegIY6hjqGOjAEmnA9qJoamoY6OhqGOppg5j6Ahj6Ahjo6HoCGPoCGPoCaID4A9n'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -5778,7 +5676,7 @@ export class BurnMintTokenPool implements c.Contract { static createCellOfTokenPoolSetAllowedDepositNamespaces(body: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }) { return TokenPool_SetAllowedDepositNamespaces.toCell(TokenPool_SetAllowedDepositNamespaces.create(body)); } @@ -5798,16 +5696,9 @@ export class BurnMintTokenPool implements c.Contract { return TokenPool_ApplyTokenTransferFeeConfigUpdates.toCell(TokenPool_ApplyTokenTransferFeeConfigUpdates.create(body)); } - static createCellOfTokenPoolUpdateRampAccess(body: { - queryId?: uint64 - updates: SnakedCell - }) { - return TokenPool_UpdateRampAccess.toCell(TokenPool_UpdateRampAccess.create(body)); - } - static createCellOfTokenPoolSetRMNProxy(body: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }) { return TokenPool_SetRMNProxy.toCell(TokenPool_SetRMNProxy.create(body)); } @@ -6084,7 +5975,7 @@ export class BurnMintTokenPool implements c.Contract { async sendTokenPoolSetAllowedDepositNamespaces(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -6116,20 +6007,9 @@ export class BurnMintTokenPool implements c.Contract { }); } - async sendTokenPoolUpdateRampAccess(provider: ContractProvider, via: Sender, msgValue: coins, body: { - queryId?: uint64 - updates: SnakedCell - }, extraOptions?: ExtraSendOptions) { - return provider.internal(via, { - value: msgValue, - body: TokenPool_UpdateRampAccess.toCell(TokenPool_UpdateRampAccess.create(body)), - ...extraOptions - }); - } - async sendTokenPoolSetRMNProxy(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -6243,29 +6123,13 @@ export class BurnMintTokenPool implements c.Contract { return r.readBoolean(); } - async getOnRamp(provider: ContractProvider, remoteChainSelector: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('onRamp', [ - { type: 'int', value: remoteChainSelector }, - ])); - return r.readNullable( - (r) => r.readSlice().loadAddress() - ); - } - - async getOffRamp(provider: ContractProvider, remoteChainSelector: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('offRamp', [ - { type: 'int', value: remoteChainSelector }, - ])); + async getRMNProxy(provider: ContractProvider): Promise { + const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); return r.readNullable( (r) => r.readSlice().loadAddress() ); } - async getRMNProxy(provider: ContractProvider): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); - return r.readSlice().loadAddress(); - } - async getVerifyNotCursed(provider: ContractProvider, subject: uint128): Promise { const r = StackReader.fromGetMethod(1, await provider.get('verifyNotCursed', [ { type: 'int', value: subject }, @@ -6310,7 +6174,10 @@ export class BurnMintTokenPool implements c.Contract { feeAdmin: r.readNullable( (r) => r.readSlice().loadAddress() ), - allowedDepositNamespaces: dictToMap(r.readDictionary(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool())), + allowedDepositNamespaces: dictToSet(r.readDictionary(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ))), }); } diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts index 2f52b629e..89b5b3072 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts @@ -1038,7 +1038,7 @@ export const CursedSubjects = { /** > struct TokenPool_AdminConfig { > ownable: Cell - > rmnProxy: address + > rmnProxy: address? > dynamicConfig: Cell > jettonClient: JettonClient > allowedFinalityConfig: uint32 @@ -1049,7 +1049,7 @@ export const CursedSubjects = { export interface TokenPool_AdminConfig { readonly $: 'TokenPool_AdminConfig' ownable: Ownable2Step - rmnProxy: c.Address + rmnProxy: c.Address | null dynamicConfig: TokenPool_DynamicConfig jettonClient: JettonClient allowedFinalityConfig: uint32 /* = 0 as uint32 */ @@ -1060,7 +1060,7 @@ export interface TokenPool_AdminConfig { export const TokenPool_AdminConfig = { create(args: { ownable: Ownable2Step - rmnProxy: c.Address + rmnProxy: c.Address | null dynamicConfig: TokenPool_DynamicConfig jettonClient: JettonClient allowedFinalityConfig?: uint32 /* = 0 as uint32 */ @@ -1079,7 +1079,7 @@ export const TokenPool_AdminConfig = { return { $: 'TokenPool_AdminConfig', ownable: loadCellRef(s, Ownable2Step.fromSlice), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), dynamicConfig: loadCellRef(s, TokenPool_DynamicConfig.fromSlice), jettonClient: JettonClient.fromSlice(s), allowedFinalityConfig: s.loadUintBig(32), @@ -1106,7 +1106,7 @@ export const TokenPool_AdminConfig = { /** > struct TokenPool_Data { > adminConfig: Cell - > mirroredPolicy: Cell + > localPolicy: Cell > tokenDecimals: uint8 > remoteChainConfigs: map > tokenTransferFeeConfigs: map @@ -1115,7 +1115,7 @@ export const TokenPool_AdminConfig = { export interface TokenPool_Data { readonly $: 'TokenPool_Data' adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -1124,7 +1124,7 @@ export interface TokenPool_Data { export const TokenPool_Data = { create(args: { adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -1138,7 +1138,7 @@ export const TokenPool_Data = { return { $: 'TokenPool_Data', adminConfig: loadCellRef(s, TokenPool_AdminConfig.fromSlice), - mirroredPolicy: loadCellRef(s, TokenPool_MirroredPolicy.fromSlice), + localPolicy: loadCellRef(s, TokenPool_LocalPolicy.fromSlice), tokenDecimals: s.loadUintBig(8), remoteChainConfigs: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store), s)), tokenTransferFeeConfigs: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store), s)), @@ -1146,7 +1146,7 @@ export const TokenPool_Data = { }, store(self: TokenPool_Data, b: c.Builder): void { storeCellRef(self.adminConfig, b, TokenPool_AdminConfig.store); - storeCellRef(self.mirroredPolicy, b, TokenPool_MirroredPolicy.store); + storeCellRef(self.localPolicy, b, TokenPool_LocalPolicy.store); b.storeUint(self.tokenDecimals, 8); b.storeDict(mapToDict(self.remoteChainConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store)), c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store)); b.storeDict(mapToDict(self.tokenTransferFeeConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store)), c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store)); @@ -1161,7 +1161,7 @@ export const TokenPool_Data = { > router: address > rateLimitAdmin: address? > feeAdmin: address? - > allowedDepositNamespaces: map + > allowedDepositNamespaces: map > } */ export interface TokenPool_DynamicConfig { @@ -1169,7 +1169,7 @@ export interface TokenPool_DynamicConfig { router: c.Address rateLimitAdmin: c.Address | null /* = null */ feeAdmin: c.Address | null - allowedDepositNamespaces: Map /* = [] as map */ + allowedDepositNamespaces: Set /* = [] as map */ } export const TokenPool_DynamicConfig = { @@ -1177,7 +1177,7 @@ export const TokenPool_DynamicConfig = { router: c.Address rateLimitAdmin?: c.Address | null /* = null */ feeAdmin: c.Address | null - allowedDepositNamespaces: Map /* = [] as map */ + allowedDepositNamespaces: Set /* = [] as map */ }): TokenPool_DynamicConfig { return { $: 'TokenPool_DynamicConfig', @@ -1191,14 +1191,23 @@ export const TokenPool_DynamicConfig = { router: s.loadAddress(), rateLimitAdmin: s.loadMaybeAddress(), feeAdmin: s.loadMaybeAddress(), - allowedDepositNamespaces: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool(), s)), + allowedDepositNamespaces: dictToSet(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), } }, store(self: TokenPool_DynamicConfig, b: c.Builder): void { b.storeAddress(self.router); b.storeAddress(self.rateLimitAdmin); b.storeAddress(self.feeAdmin); - b.storeDict(mapToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()), c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()); + b.storeDict(setToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); }, toCell(self: TokenPool_DynamicConfig): c.Cell { return makeCellFrom(self, TokenPool_DynamicConfig.store); @@ -1206,108 +1215,35 @@ export const TokenPool_DynamicConfig = { } /** - > struct TokenPool_MirroredPolicy { - > onRamps: map - > offRamps: map + > struct TokenPool_LocalPolicy { > cursedSubjects: CursedSubjects > } */ -export interface TokenPool_MirroredPolicy { - readonly $: 'TokenPool_MirroredPolicy' - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ +export interface TokenPool_LocalPolicy { + readonly $: 'TokenPool_LocalPolicy' cursedSubjects: CursedSubjects } -export const TokenPool_MirroredPolicy = { +export const TokenPool_LocalPolicy = { create(args: { - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ cursedSubjects: CursedSubjects - }): TokenPool_MirroredPolicy { + }): TokenPool_LocalPolicy { return { - $: 'TokenPool_MirroredPolicy', + $: 'TokenPool_LocalPolicy', ...args } }, - fromSlice(s: c.Slice): TokenPool_MirroredPolicy { + fromSlice(s: c.Slice): TokenPool_LocalPolicy { return { - $: 'TokenPool_MirroredPolicy', - onRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), - offRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), + $: 'TokenPool_LocalPolicy', cursedSubjects: CursedSubjects.fromSlice(s), } }, - store(self: TokenPool_MirroredPolicy, b: c.Builder): void { - b.storeDict(mapToDict(self.onRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); - b.storeDict(mapToDict(self.offRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); + store(self: TokenPool_LocalPolicy, b: c.Builder): void { CursedSubjects.store(self.cursedSubjects, b); }, - toCell(self: TokenPool_MirroredPolicy): c.Cell { - return makeCellFrom(self, TokenPool_MirroredPolicy.store); - } -} - -/** - > struct TokenPool_RampUpdate { - > remoteChainSelector: uint64 - > onRamp: address? - > offRamp: address? - > } - */ -export interface TokenPool_RampUpdate { - readonly $: 'TokenPool_RampUpdate' - remoteChainSelector: uint64 - onRamp: c.Address | null /* = null */ - offRamp: c.Address | null /* = null */ -} - -export const TokenPool_RampUpdate = { - create(args: { - remoteChainSelector: uint64 - onRamp?: c.Address | null /* = null */ - offRamp?: c.Address | null /* = null */ - }): TokenPool_RampUpdate { - return { - $: 'TokenPool_RampUpdate', - onRamp: null, - offRamp: null, - ...args - } - }, - fromSlice(s: c.Slice): TokenPool_RampUpdate { - return { - $: 'TokenPool_RampUpdate', - remoteChainSelector: s.loadUintBig(64), - onRamp: s.loadMaybeAddress(), - offRamp: s.loadMaybeAddress(), - } - }, - store(self: TokenPool_RampUpdate, b: c.Builder): void { - b.storeUint(self.remoteChainSelector, 64); - b.storeAddress(self.onRamp); - b.storeAddress(self.offRamp); - }, - toCell(self: TokenPool_RampUpdate): c.Cell { - return makeCellFrom(self, TokenPool_RampUpdate.store); + toCell(self: TokenPool_LocalPolicy): c.Cell { + return makeCellFrom(self, TokenPool_LocalPolicy.store); } } @@ -2420,13 +2356,13 @@ export const TokenPool_DeployableCodeSet = { /** > struct (0x84384142) TokenPool_SetAllowedDepositNamespaces { > queryId: uint64 - > allowedDepositNamespaces: map + > allowedDepositNamespaces: map > } */ export interface TokenPool_SetAllowedDepositNamespaces { readonly $: 'TokenPool_SetAllowedDepositNamespaces' queryId: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set } export const TokenPool_SetAllowedDepositNamespaces = { @@ -2434,7 +2370,7 @@ export const TokenPool_SetAllowedDepositNamespaces = { create(args: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }): TokenPool_SetAllowedDepositNamespaces { return { $: 'TokenPool_SetAllowedDepositNamespaces', @@ -2447,13 +2383,22 @@ export const TokenPool_SetAllowedDepositNamespaces = { return { $: 'TokenPool_SetAllowedDepositNamespaces', queryId: s.loadUintBig(64), - allowedDepositNamespaces: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool(), s)), + allowedDepositNamespaces: dictToSet(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), } }, store(self: TokenPool_SetAllowedDepositNamespaces, b: c.Builder): void { b.storeUint(0x84384142, 32); b.storeUint(self.queryId, 64); - b.storeDict(mapToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()), c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()); + b.storeDict(setToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); }, toCell(self: TokenPool_SetAllowedDepositNamespaces): c.Cell { return makeCellFrom(self, TokenPool_SetAllowedDepositNamespaces.store); @@ -2589,59 +2534,16 @@ export const TokenPool_ApplyTokenTransferFeeConfigUpdates = { } } -/** - > struct (0xe30764be) TokenPool_UpdateRampAccess { - > queryId: uint64 - > updates: SnakedCell - > } - */ -export interface TokenPool_UpdateRampAccess { - readonly $: 'TokenPool_UpdateRampAccess' - queryId: uint64 - updates: SnakedCell -} - -export const TokenPool_UpdateRampAccess = { - PREFIX: 0xe30764be, - - create(args: { - queryId?: uint64 - updates: SnakedCell - }): TokenPool_UpdateRampAccess { - return { - $: 'TokenPool_UpdateRampAccess', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_UpdateRampAccess { - loadAndCheckPrefix32(s, 0xe30764be, 'TokenPool_UpdateRampAccess'); - return { - $: 'TokenPool_UpdateRampAccess', - queryId: s.loadUintBig(64), - updates: loadSnakedCellOf(s, TokenPool_RampUpdate.fromSlice), - } - }, - store(self: TokenPool_UpdateRampAccess, b: c.Builder): void { - b.storeUint(0xe30764be, 32); - b.storeUint(self.queryId, 64); - storeSnakedCellOf(self.updates, b, TokenPool_RampUpdate.store); - }, - toCell(self: TokenPool_UpdateRampAccess): c.Cell { - return makeCellFrom(self, TokenPool_UpdateRampAccess.store); - } -} - /** > struct (0x9929b642) TokenPool_SetRMNProxy { > queryId: uint64 - > rmnProxy: address + > rmnProxy: address? > } */ export interface TokenPool_SetRMNProxy { readonly $: 'TokenPool_SetRMNProxy' queryId: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null } export const TokenPool_SetRMNProxy = { @@ -2649,7 +2551,7 @@ export const TokenPool_SetRMNProxy = { create(args: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }): TokenPool_SetRMNProxy { return { $: 'TokenPool_SetRMNProxy', @@ -2662,7 +2564,7 @@ export const TokenPool_SetRMNProxy = { return { $: 'TokenPool_SetRMNProxy', queryId: s.loadUintBig(64), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), } }, store(self: TokenPool_SetRMNProxy, b: c.Builder): void { @@ -3943,13 +3845,13 @@ export const TokenPool_RateLimitConfiguredNotification = { /** > struct (0xe5d08b2e) TokenPool_RMNProxySet { > queryId: uint64 - > rmnProxy: address + > rmnProxy: address? > } */ export interface TokenPool_RMNProxySet { readonly $: 'TokenPool_RMNProxySet' queryId: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null } export const TokenPool_RMNProxySet = { @@ -3957,7 +3859,7 @@ export const TokenPool_RMNProxySet = { create(args: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }): TokenPool_RMNProxySet { return { $: 'TokenPool_RMNProxySet', @@ -3970,7 +3872,7 @@ export const TokenPool_RMNProxySet = { return { $: 'TokenPool_RMNProxySet', queryId: s.loadUintBig(64), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), } }, store(self: TokenPool_RMNProxySet, b: c.Builder): void { @@ -4064,44 +3966,6 @@ export const TokenPool_ChainUpdatesApplied = { } } -/** - > struct (0xd7f5c563) TokenPool_RampAccessUpdatesApplied { - > queryId: uint64 - > } - */ -export interface TokenPool_RampAccessUpdatesApplied { - readonly $: 'TokenPool_RampAccessUpdatesApplied' - queryId: uint64 -} - -export const TokenPool_RampAccessUpdatesApplied = { - PREFIX: 0xd7f5c563, - - create(args: { - queryId?: uint64 - }): TokenPool_RampAccessUpdatesApplied { - return { - $: 'TokenPool_RampAccessUpdatesApplied', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_RampAccessUpdatesApplied { - loadAndCheckPrefix32(s, 0xd7f5c563, 'TokenPool_RampAccessUpdatesApplied'); - return { - $: 'TokenPool_RampAccessUpdatesApplied', - queryId: s.loadUintBig(64), - } - }, - store(self: TokenPool_RampAccessUpdatesApplied, b: c.Builder): void { - b.storeUint(0xd7f5c563, 32); - b.storeUint(self.queryId, 64); - }, - toCell(self: TokenPool_RampAccessUpdatesApplied): c.Cell { - return makeCellFrom(self, TokenPool_RampAccessUpdatesApplied.store); - } -} - /** > struct (0x28cbcc64) TokenPool_FeeConfigApplied { > queryId: uint64 @@ -4420,6 +4284,80 @@ export const JettonLockBox_WithdrawFailed = { } } +/** + > struct (0x60fdb63b) LockReleaseLockboxTokenPool_LockContext { + > forwardPayload: Cell + > } + */ +export interface LockReleaseLockboxTokenPool_LockContext { + readonly $: 'LockReleaseLockboxTokenPool_LockContext' + forwardPayload: TokenPool_LockOrBurnForwardPayload +} + +export const LockReleaseLockboxTokenPool_LockContext = { + PREFIX: 0x60fdb63b, + + create(args: { + forwardPayload: TokenPool_LockOrBurnForwardPayload + }): LockReleaseLockboxTokenPool_LockContext { + return { + $: 'LockReleaseLockboxTokenPool_LockContext', + ...args + } + }, + fromSlice(s: c.Slice): LockReleaseLockboxTokenPool_LockContext { + loadAndCheckPrefix32(s, 0x60fdb63b, 'LockReleaseLockboxTokenPool_LockContext'); + return { + $: 'LockReleaseLockboxTokenPool_LockContext', + forwardPayload: loadCellRef(s, TokenPool_LockOrBurnForwardPayload.fromSlice), + } + }, + store(self: LockReleaseLockboxTokenPool_LockContext, b: c.Builder): void { + b.storeUint(0x60fdb63b, 32); + storeCellRef(self.forwardPayload, b, TokenPool_LockOrBurnForwardPayload.store); + }, + toCell(self: LockReleaseLockboxTokenPool_LockContext): c.Cell { + return makeCellFrom(self, LockReleaseLockboxTokenPool_LockContext.store); + } +} + +/** + > struct (0x90230477) LockReleaseLockboxTokenPool_ReleaseContext { + > forwardPayload: Cell + > } + */ +export interface LockReleaseLockboxTokenPool_ReleaseContext { + readonly $: 'LockReleaseLockboxTokenPool_ReleaseContext' + forwardPayload: TokenPool_ReleaseOrMintForwardPayload +} + +export const LockReleaseLockboxTokenPool_ReleaseContext = { + PREFIX: 0x90230477, + + create(args: { + forwardPayload: TokenPool_ReleaseOrMintForwardPayload + }): LockReleaseLockboxTokenPool_ReleaseContext { + return { + $: 'LockReleaseLockboxTokenPool_ReleaseContext', + ...args + } + }, + fromSlice(s: c.Slice): LockReleaseLockboxTokenPool_ReleaseContext { + loadAndCheckPrefix32(s, 0x90230477, 'LockReleaseLockboxTokenPool_ReleaseContext'); + return { + $: 'LockReleaseLockboxTokenPool_ReleaseContext', + forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), + } + }, + store(self: LockReleaseLockboxTokenPool_ReleaseContext, b: c.Builder): void { + b.storeUint(0x90230477, 32); + storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); + }, + toCell(self: LockReleaseLockboxTokenPool_ReleaseContext): c.Cell { + return makeCellFrom(self, LockReleaseLockboxTokenPool_ReleaseContext.store); + } +} + /** > struct Storage { > poolData: Cell @@ -5351,51 +5289,6 @@ export const TokenPool_RateLimitConfigured = { } } -/** - > struct TokenPool_RampAccessUpdated { - > remoteChainSelector: uint64 - > onRamp: address? - > offRamp: address? - > } - */ -export interface TokenPool_RampAccessUpdated { - readonly $: 'TokenPool_RampAccessUpdated' - remoteChainSelector: uint64 - onRamp: c.Address | null /* = null */ - offRamp: c.Address | null /* = null */ -} - -export const TokenPool_RampAccessUpdated = { - create(args: { - remoteChainSelector: uint64 - onRamp?: c.Address | null /* = null */ - offRamp?: c.Address | null /* = null */ - }): TokenPool_RampAccessUpdated { - return { - $: 'TokenPool_RampAccessUpdated', - onRamp: null, - offRamp: null, - ...args - } - }, - fromSlice(s: c.Slice): TokenPool_RampAccessUpdated { - return { - $: 'TokenPool_RampAccessUpdated', - remoteChainSelector: s.loadUintBig(64), - onRamp: s.loadMaybeAddress(), - offRamp: s.loadMaybeAddress(), - } - }, - store(self: TokenPool_RampAccessUpdated, b: c.Builder): void { - b.storeUint(self.remoteChainSelector, 64); - b.storeAddress(self.onRamp); - b.storeAddress(self.offRamp); - }, - toCell(self: TokenPool_RampAccessUpdated): c.Cell { - return makeCellFrom(self, TokenPool_RampAccessUpdated.store); - } -} - /** > struct RateLimiter_Config { > isEnabled: bool @@ -5531,7 +5424,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseLockboxTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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93HPC2HMyXD7AAAkghAw66vbzwv3cc8LYczJcPsAAgEgAH8AgAIBIACZAJoCASAAgQCCAgEgAJIAkwIBIACDAIQCASAAkACRAfEJsMAlSxus8MAkXDijh9XFAgRFggHERUHBhEUBlUEERMr2pMCERACUP6BAIcO4BEWERwRFhEVERsRFREUERoRFBETERkRExESERgREhERERcREREQERwREA8RGw8OERoODREZDQwRGAwLERcLChEcCgkRGwkIERoIgAIUB9wmwwCVK26zwwCRcOKOKFcUCBEWCAcRFQcGERQGVQQREyramAcRFQcGERQGBRETBYEAhxETVUDgI9DT/9TRINDTP/pIMfoA+kgwggDJ/VYi0NQx+kgx1DH6SDBYxwXy9IIAyfkiVh+AQPQOb6Ex8vRWIFYgViBWIFYgViCAAiAH+BxEZBwYRGAYFERcFBBEcBAMRGwMCERoCAREZAREYVhdWHVYdVh1WHVYd8BEEjjhfBFcXVxdXF1cXVxdXFxEQERYREA8RFQ8OERQODRETDQwREgwLERELChEQChCfEI4QfRBsVVVVBODIz5Pp9pESViHPCz8BESABzAERHgHLHwCGAfwBERwB9ABWGgH6VMnIAREc+gIBERz6AgERHAHMAREaAczJyM+Tno1BBgERHAHLPwERFgH6UgERFwHMAREZAczJyM+FiAERFQH6UnHPC24BERQBzMmAQPsAERARFhEQDxEVDw4RFA4NERMNDBESDAsREQsKERAKEJ8QjhB9EGwAhwAYEFsQShA5SBZFVQcDAfxWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgVhjwElYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYZ8BNWIAJWIAJWIAJWIAJWIAJWIAJWIAJWIAJWIAJWIAJWIAJWIAIAiQP8ViACViACViACViACViACViACViACViACViACViACViACAREaAREZVh3wFFEioSXjD4IAyfhRIYBA9A5voRPy9AHU9AQx1DHUMdFWHcjL/8nIz5Pp9pESKc8LPyjPFCfPCx9SYPQAUlD6VMnIJfoCVh/6AiPPFCLPFMkrViMJAIoAiwCMAv5WH9DUMfpIMdQx+kgx1DHTH/pQMfQEMdFSYPAWggDJ+CJW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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -5755,7 +5648,7 @@ export class LockReleaseLockboxTokenPool implements c.Contract { static createCellOfTokenPoolSetAllowedDepositNamespaces(body: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }) { return TokenPool_SetAllowedDepositNamespaces.toCell(TokenPool_SetAllowedDepositNamespaces.create(body)); } @@ -5775,16 +5668,9 @@ export class LockReleaseLockboxTokenPool implements c.Contract { return TokenPool_ApplyTokenTransferFeeConfigUpdates.toCell(TokenPool_ApplyTokenTransferFeeConfigUpdates.create(body)); } - static createCellOfTokenPoolUpdateRampAccess(body: { - queryId?: uint64 - updates: SnakedCell - }) { - return TokenPool_UpdateRampAccess.toCell(TokenPool_UpdateRampAccess.create(body)); - } - static createCellOfTokenPoolSetRMNProxy(body: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }) { return TokenPool_SetRMNProxy.toCell(TokenPool_SetRMNProxy.create(body)); } @@ -6073,7 +5959,7 @@ export class LockReleaseLockboxTokenPool implements c.Contract { async sendTokenPoolSetAllowedDepositNamespaces(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -6105,20 +5991,9 @@ export class LockReleaseLockboxTokenPool implements c.Contract { }); } - async sendTokenPoolUpdateRampAccess(provider: ContractProvider, via: Sender, msgValue: coins, body: { - queryId?: uint64 - updates: SnakedCell - }, extraOptions?: ExtraSendOptions) { - return provider.internal(via, { - value: msgValue, - body: TokenPool_UpdateRampAccess.toCell(TokenPool_UpdateRampAccess.create(body)), - ...extraOptions - }); - } - async sendTokenPoolSetRMNProxy(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -6253,29 +6128,13 @@ export class LockReleaseLockboxTokenPool implements c.Contract { return r.readBoolean(); } - async getOnRamp(provider: ContractProvider, remoteChainSelector: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('onRamp', [ - { type: 'int', value: remoteChainSelector }, - ])); - return r.readNullable( - (r) => r.readSlice().loadAddress() - ); - } - - async getOffRamp(provider: ContractProvider, remoteChainSelector: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('offRamp', [ - { type: 'int', value: remoteChainSelector }, - ])); + async getRMNProxy(provider: ContractProvider): Promise { + const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); return r.readNullable( (r) => r.readSlice().loadAddress() ); } - async getRMNProxy(provider: ContractProvider): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); - return r.readSlice().loadAddress(); - } - async getVerifyNotCursed(provider: ContractProvider, subject: uint128): Promise { const r = StackReader.fromGetMethod(1, await provider.get('verifyNotCursed', [ { type: 'int', value: subject }, @@ -6320,7 +6179,10 @@ export class LockReleaseLockboxTokenPool implements c.Contract { feeAdmin: r.readNullable( (r) => r.readSlice().loadAddress() ), - allowedDepositNamespaces: dictToMap(r.readDictionary(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool())), + allowedDepositNamespaces: dictToSet(r.readDictionary(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ))), }); } diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts index 5e4cc5728..679089795 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts @@ -1160,7 +1160,7 @@ export const CursedSubjects = { /** > struct TokenPool_AdminConfig { > ownable: Cell - > rmnProxy: address + > rmnProxy: address? > dynamicConfig: Cell > jettonClient: JettonClient > allowedFinalityConfig: uint32 @@ -1171,7 +1171,7 @@ export const CursedSubjects = { export interface TokenPool_AdminConfig { readonly $: 'TokenPool_AdminConfig' ownable: Ownable2Step - rmnProxy: c.Address + rmnProxy: c.Address | null dynamicConfig: TokenPool_DynamicConfig jettonClient: JettonClient allowedFinalityConfig: uint32 /* = 0 as uint32 */ @@ -1182,7 +1182,7 @@ export interface TokenPool_AdminConfig { export const TokenPool_AdminConfig = { create(args: { ownable: Ownable2Step - rmnProxy: c.Address + rmnProxy: c.Address | null dynamicConfig: TokenPool_DynamicConfig jettonClient: JettonClient allowedFinalityConfig?: uint32 /* = 0 as uint32 */ @@ -1201,7 +1201,7 @@ export const TokenPool_AdminConfig = { return { $: 'TokenPool_AdminConfig', ownable: loadCellRef(s, Ownable2Step.fromSlice), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), dynamicConfig: loadCellRef(s, TokenPool_DynamicConfig.fromSlice), jettonClient: JettonClient.fromSlice(s), allowedFinalityConfig: s.loadUintBig(32), @@ -1228,7 +1228,7 @@ export const TokenPool_AdminConfig = { /** > struct TokenPool_Data { > adminConfig: Cell - > mirroredPolicy: Cell + > localPolicy: Cell > tokenDecimals: uint8 > remoteChainConfigs: map > tokenTransferFeeConfigs: map @@ -1237,7 +1237,7 @@ export const TokenPool_AdminConfig = { export interface TokenPool_Data { readonly $: 'TokenPool_Data' adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -1246,7 +1246,7 @@ export interface TokenPool_Data { export const TokenPool_Data = { create(args: { adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -1260,7 +1260,7 @@ export const TokenPool_Data = { return { $: 'TokenPool_Data', adminConfig: loadCellRef(s, TokenPool_AdminConfig.fromSlice), - mirroredPolicy: loadCellRef(s, TokenPool_MirroredPolicy.fromSlice), + localPolicy: loadCellRef(s, TokenPool_LocalPolicy.fromSlice), tokenDecimals: s.loadUintBig(8), remoteChainConfigs: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store), s)), tokenTransferFeeConfigs: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store), s)), @@ -1268,7 +1268,7 @@ export const TokenPool_Data = { }, store(self: TokenPool_Data, b: c.Builder): void { storeCellRef(self.adminConfig, b, TokenPool_AdminConfig.store); - storeCellRef(self.mirroredPolicy, b, TokenPool_MirroredPolicy.store); + storeCellRef(self.localPolicy, b, TokenPool_LocalPolicy.store); b.storeUint(self.tokenDecimals, 8); b.storeDict(mapToDict(self.remoteChainConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store)), c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store)); b.storeDict(mapToDict(self.tokenTransferFeeConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store)), c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store)); @@ -1283,7 +1283,7 @@ export const TokenPool_Data = { > router: address > rateLimitAdmin: address? > feeAdmin: address? - > allowedDepositNamespaces: map + > allowedDepositNamespaces: map > } */ export interface TokenPool_DynamicConfig { @@ -1291,7 +1291,7 @@ export interface TokenPool_DynamicConfig { router: c.Address rateLimitAdmin: c.Address | null /* = null */ feeAdmin: c.Address | null - allowedDepositNamespaces: Map /* = [] as map */ + allowedDepositNamespaces: Set /* = [] as map */ } export const TokenPool_DynamicConfig = { @@ -1299,7 +1299,7 @@ export const TokenPool_DynamicConfig = { router: c.Address rateLimitAdmin?: c.Address | null /* = null */ feeAdmin: c.Address | null - allowedDepositNamespaces: Map /* = [] as map */ + allowedDepositNamespaces: Set /* = [] as map */ }): TokenPool_DynamicConfig { return { $: 'TokenPool_DynamicConfig', @@ -1313,14 +1313,23 @@ export const TokenPool_DynamicConfig = { router: s.loadAddress(), rateLimitAdmin: s.loadMaybeAddress(), feeAdmin: s.loadMaybeAddress(), - allowedDepositNamespaces: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool(), s)), + allowedDepositNamespaces: dictToSet(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), } }, store(self: TokenPool_DynamicConfig, b: c.Builder): void { b.storeAddress(self.router); b.storeAddress(self.rateLimitAdmin); b.storeAddress(self.feeAdmin); - b.storeDict(mapToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()), c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()); + b.storeDict(setToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); }, toCell(self: TokenPool_DynamicConfig): c.Cell { return makeCellFrom(self, TokenPool_DynamicConfig.store); @@ -1328,108 +1337,35 @@ export const TokenPool_DynamicConfig = { } /** - > struct TokenPool_MirroredPolicy { - > onRamps: map - > offRamps: map + > struct TokenPool_LocalPolicy { > cursedSubjects: CursedSubjects > } */ -export interface TokenPool_MirroredPolicy { - readonly $: 'TokenPool_MirroredPolicy' - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ +export interface TokenPool_LocalPolicy { + readonly $: 'TokenPool_LocalPolicy' cursedSubjects: CursedSubjects } -export const TokenPool_MirroredPolicy = { +export const TokenPool_LocalPolicy = { create(args: { - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ cursedSubjects: CursedSubjects - }): TokenPool_MirroredPolicy { + }): TokenPool_LocalPolicy { return { - $: 'TokenPool_MirroredPolicy', + $: 'TokenPool_LocalPolicy', ...args } }, - fromSlice(s: c.Slice): TokenPool_MirroredPolicy { + fromSlice(s: c.Slice): TokenPool_LocalPolicy { return { - $: 'TokenPool_MirroredPolicy', - onRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), - offRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), + $: 'TokenPool_LocalPolicy', cursedSubjects: CursedSubjects.fromSlice(s), } }, - store(self: TokenPool_MirroredPolicy, b: c.Builder): void { - b.storeDict(mapToDict(self.onRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); - b.storeDict(mapToDict(self.offRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); + store(self: TokenPool_LocalPolicy, b: c.Builder): void { CursedSubjects.store(self.cursedSubjects, b); }, - toCell(self: TokenPool_MirroredPolicy): c.Cell { - return makeCellFrom(self, TokenPool_MirroredPolicy.store); - } -} - -/** - > struct TokenPool_RampUpdate { - > remoteChainSelector: uint64 - > onRamp: address? - > offRamp: address? - > } - */ -export interface TokenPool_RampUpdate { - readonly $: 'TokenPool_RampUpdate' - remoteChainSelector: uint64 - onRamp: c.Address | null /* = null */ - offRamp: c.Address | null /* = null */ -} - -export const TokenPool_RampUpdate = { - create(args: { - remoteChainSelector: uint64 - onRamp?: c.Address | null /* = null */ - offRamp?: c.Address | null /* = null */ - }): TokenPool_RampUpdate { - return { - $: 'TokenPool_RampUpdate', - onRamp: null, - offRamp: null, - ...args - } - }, - fromSlice(s: c.Slice): TokenPool_RampUpdate { - return { - $: 'TokenPool_RampUpdate', - remoteChainSelector: s.loadUintBig(64), - onRamp: s.loadMaybeAddress(), - offRamp: s.loadMaybeAddress(), - } - }, - store(self: TokenPool_RampUpdate, b: c.Builder): void { - b.storeUint(self.remoteChainSelector, 64); - b.storeAddress(self.onRamp); - b.storeAddress(self.offRamp); - }, - toCell(self: TokenPool_RampUpdate): c.Cell { - return makeCellFrom(self, TokenPool_RampUpdate.store); + toCell(self: TokenPool_LocalPolicy): c.Cell { + return makeCellFrom(self, TokenPool_LocalPolicy.store); } } @@ -2542,13 +2478,13 @@ export const TokenPool_DeployableCodeSet = { /** > struct (0x84384142) TokenPool_SetAllowedDepositNamespaces { > queryId: uint64 - > allowedDepositNamespaces: map + > allowedDepositNamespaces: map > } */ export interface TokenPool_SetAllowedDepositNamespaces { readonly $: 'TokenPool_SetAllowedDepositNamespaces' queryId: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set } export const TokenPool_SetAllowedDepositNamespaces = { @@ -2556,7 +2492,7 @@ export const TokenPool_SetAllowedDepositNamespaces = { create(args: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }): TokenPool_SetAllowedDepositNamespaces { return { $: 'TokenPool_SetAllowedDepositNamespaces', @@ -2569,13 +2505,22 @@ export const TokenPool_SetAllowedDepositNamespaces = { return { $: 'TokenPool_SetAllowedDepositNamespaces', queryId: s.loadUintBig(64), - allowedDepositNamespaces: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool(), s)), + allowedDepositNamespaces: dictToSet(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), } }, store(self: TokenPool_SetAllowedDepositNamespaces, b: c.Builder): void { b.storeUint(0x84384142, 32); b.storeUint(self.queryId, 64); - b.storeDict(mapToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()), c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()); + b.storeDict(setToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); }, toCell(self: TokenPool_SetAllowedDepositNamespaces): c.Cell { return makeCellFrom(self, TokenPool_SetAllowedDepositNamespaces.store); @@ -2711,59 +2656,16 @@ export const TokenPool_ApplyTokenTransferFeeConfigUpdates = { } } -/** - > struct (0xe30764be) TokenPool_UpdateRampAccess { - > queryId: uint64 - > updates: SnakedCell - > } - */ -export interface TokenPool_UpdateRampAccess { - readonly $: 'TokenPool_UpdateRampAccess' - queryId: uint64 - updates: SnakedCell -} - -export const TokenPool_UpdateRampAccess = { - PREFIX: 0xe30764be, - - create(args: { - queryId?: uint64 - updates: SnakedCell - }): TokenPool_UpdateRampAccess { - return { - $: 'TokenPool_UpdateRampAccess', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_UpdateRampAccess { - loadAndCheckPrefix32(s, 0xe30764be, 'TokenPool_UpdateRampAccess'); - return { - $: 'TokenPool_UpdateRampAccess', - queryId: s.loadUintBig(64), - updates: loadSnakedCellOf(s, TokenPool_RampUpdate.fromSlice), - } - }, - store(self: TokenPool_UpdateRampAccess, b: c.Builder): void { - b.storeUint(0xe30764be, 32); - b.storeUint(self.queryId, 64); - storeSnakedCellOf(self.updates, b, TokenPool_RampUpdate.store); - }, - toCell(self: TokenPool_UpdateRampAccess): c.Cell { - return makeCellFrom(self, TokenPool_UpdateRampAccess.store); - } -} - /** > struct (0x9929b642) TokenPool_SetRMNProxy { > queryId: uint64 - > rmnProxy: address + > rmnProxy: address? > } */ export interface TokenPool_SetRMNProxy { readonly $: 'TokenPool_SetRMNProxy' queryId: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null } export const TokenPool_SetRMNProxy = { @@ -2771,7 +2673,7 @@ export const TokenPool_SetRMNProxy = { create(args: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }): TokenPool_SetRMNProxy { return { $: 'TokenPool_SetRMNProxy', @@ -2784,7 +2686,7 @@ export const TokenPool_SetRMNProxy = { return { $: 'TokenPool_SetRMNProxy', queryId: s.loadUintBig(64), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), } }, store(self: TokenPool_SetRMNProxy, b: c.Builder): void { @@ -4065,13 +3967,13 @@ export const TokenPool_RateLimitConfiguredNotification = { /** > struct (0xe5d08b2e) TokenPool_RMNProxySet { > queryId: uint64 - > rmnProxy: address + > rmnProxy: address? > } */ export interface TokenPool_RMNProxySet { readonly $: 'TokenPool_RMNProxySet' queryId: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null } export const TokenPool_RMNProxySet = { @@ -4079,7 +3981,7 @@ export const TokenPool_RMNProxySet = { create(args: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }): TokenPool_RMNProxySet { return { $: 'TokenPool_RMNProxySet', @@ -4092,7 +3994,7 @@ export const TokenPool_RMNProxySet = { return { $: 'TokenPool_RMNProxySet', queryId: s.loadUintBig(64), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), } }, store(self: TokenPool_RMNProxySet, b: c.Builder): void { @@ -4186,44 +4088,6 @@ export const TokenPool_ChainUpdatesApplied = { } } -/** - > struct (0xd7f5c563) TokenPool_RampAccessUpdatesApplied { - > queryId: uint64 - > } - */ -export interface TokenPool_RampAccessUpdatesApplied { - readonly $: 'TokenPool_RampAccessUpdatesApplied' - queryId: uint64 -} - -export const TokenPool_RampAccessUpdatesApplied = { - PREFIX: 0xd7f5c563, - - create(args: { - queryId?: uint64 - }): TokenPool_RampAccessUpdatesApplied { - return { - $: 'TokenPool_RampAccessUpdatesApplied', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_RampAccessUpdatesApplied { - loadAndCheckPrefix32(s, 0xd7f5c563, 'TokenPool_RampAccessUpdatesApplied'); - return { - $: 'TokenPool_RampAccessUpdatesApplied', - queryId: s.loadUintBig(64), - } - }, - store(self: TokenPool_RampAccessUpdatesApplied, b: c.Builder): void { - b.storeUint(0xd7f5c563, 32); - b.storeUint(self.queryId, 64); - }, - toCell(self: TokenPool_RampAccessUpdatesApplied): c.Cell { - return makeCellFrom(self, TokenPool_RampAccessUpdatesApplied.store); - } -} - /** > struct (0x28cbcc64) TokenPool_FeeConfigApplied { > queryId: uint64 @@ -5072,47 +4936,39 @@ export const TokenPool_RateLimitConfigured = { } /** - > struct TokenPool_RampAccessUpdated { - > remoteChainSelector: uint64 - > onRamp: address? - > offRamp: address? + > struct (0xed696f9b) LockReleaseTokenPool_ReleaseContext { + > forwardPayload: Cell > } */ -export interface TokenPool_RampAccessUpdated { - readonly $: 'TokenPool_RampAccessUpdated' - remoteChainSelector: uint64 - onRamp: c.Address | null /* = null */ - offRamp: c.Address | null /* = null */ +export interface LockReleaseTokenPool_ReleaseContext { + readonly $: 'LockReleaseTokenPool_ReleaseContext' + forwardPayload: TokenPool_ReleaseOrMintForwardPayload } -export const TokenPool_RampAccessUpdated = { +export const LockReleaseTokenPool_ReleaseContext = { + PREFIX: 0xed696f9b, + create(args: { - remoteChainSelector: uint64 - onRamp?: c.Address | null /* = null */ - offRamp?: c.Address | null /* = null */ - }): TokenPool_RampAccessUpdated { + forwardPayload: TokenPool_ReleaseOrMintForwardPayload + }): LockReleaseTokenPool_ReleaseContext { return { - $: 'TokenPool_RampAccessUpdated', - onRamp: null, - offRamp: null, + $: 'LockReleaseTokenPool_ReleaseContext', ...args } }, - fromSlice(s: c.Slice): TokenPool_RampAccessUpdated { + fromSlice(s: c.Slice): LockReleaseTokenPool_ReleaseContext { + loadAndCheckPrefix32(s, 0xed696f9b, 'LockReleaseTokenPool_ReleaseContext'); return { - $: 'TokenPool_RampAccessUpdated', - remoteChainSelector: s.loadUintBig(64), - onRamp: s.loadMaybeAddress(), - offRamp: s.loadMaybeAddress(), + $: 'LockReleaseTokenPool_ReleaseContext', + forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), } }, - store(self: TokenPool_RampAccessUpdated, b: c.Builder): void { - b.storeUint(self.remoteChainSelector, 64); - b.storeAddress(self.onRamp); - b.storeAddress(self.offRamp); + store(self: LockReleaseTokenPool_ReleaseContext, b: c.Builder): void { + b.storeUint(0xed696f9b, 32); + storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); }, - toCell(self: TokenPool_RampAccessUpdated): c.Cell { - return makeCellFrom(self, TokenPool_RampAccessUpdated.store); + toCell(self: LockReleaseTokenPool_ReleaseContext): c.Cell { + return makeCellFrom(self, LockReleaseTokenPool_ReleaseContext.store); } } @@ -5294,7 +5150,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -5516,7 +5372,7 @@ export class LockReleaseTokenPool implements c.Contract { static createCellOfTokenPoolSetAllowedDepositNamespaces(body: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }) { return TokenPool_SetAllowedDepositNamespaces.toCell(TokenPool_SetAllowedDepositNamespaces.create(body)); } @@ -5536,16 +5392,9 @@ export class LockReleaseTokenPool implements c.Contract { return TokenPool_ApplyTokenTransferFeeConfigUpdates.toCell(TokenPool_ApplyTokenTransferFeeConfigUpdates.create(body)); } - static createCellOfTokenPoolUpdateRampAccess(body: { - queryId?: uint64 - updates: SnakedCell - }) { - return TokenPool_UpdateRampAccess.toCell(TokenPool_UpdateRampAccess.create(body)); - } - static createCellOfTokenPoolSetRMNProxy(body: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }) { return TokenPool_SetRMNProxy.toCell(TokenPool_SetRMNProxy.create(body)); } @@ -5814,7 +5663,7 @@ export class LockReleaseTokenPool implements c.Contract { async sendTokenPoolSetAllowedDepositNamespaces(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -5846,20 +5695,9 @@ export class LockReleaseTokenPool implements c.Contract { }); } - async sendTokenPoolUpdateRampAccess(provider: ContractProvider, via: Sender, msgValue: coins, body: { - queryId?: uint64 - updates: SnakedCell - }, extraOptions?: ExtraSendOptions) { - return provider.internal(via, { - value: msgValue, - body: TokenPool_UpdateRampAccess.toCell(TokenPool_UpdateRampAccess.create(body)), - ...extraOptions - }); - } - async sendTokenPoolSetRMNProxy(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -5961,29 +5799,13 @@ export class LockReleaseTokenPool implements c.Contract { return r.readBoolean(); } - async getOnRamp(provider: ContractProvider, remoteChainSelector: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('onRamp', [ - { type: 'int', value: remoteChainSelector }, - ])); - return r.readNullable( - (r) => r.readSlice().loadAddress() - ); - } - - async getOffRamp(provider: ContractProvider, remoteChainSelector: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('offRamp', [ - { type: 'int', value: remoteChainSelector }, - ])); + async getRMNProxy(provider: ContractProvider): Promise { + const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); return r.readNullable( (r) => r.readSlice().loadAddress() ); } - async getRMNProxy(provider: ContractProvider): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); - return r.readSlice().loadAddress(); - } - async getVerifyNotCursed(provider: ContractProvider, subject: uint128): Promise { const r = StackReader.fromGetMethod(1, await provider.get('verifyNotCursed', [ { type: 'int', value: subject }, @@ -6035,7 +5857,10 @@ export class LockReleaseTokenPool implements c.Contract { feeAdmin: r.readNullable( (r) => r.readSlice().loadAddress() ), - allowedDepositNamespaces: dictToMap(r.readDictionary(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool())), + allowedDepositNamespaces: dictToSet(r.readDictionary(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ))), }); } diff --git a/contracts/wrappers/gen/ccip/pools/TokenPool.ts b/contracts/wrappers/gen/ccip/pools/TokenPool.ts index aab3daeef..63d6a2b8e 100644 --- a/contracts/wrappers/gen/ccip/pools/TokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/TokenPool.ts @@ -352,7 +352,7 @@ export const CrossChainAddress = { > router: address > rateLimitAdmin: address? > feeAdmin: address? - > allowedDepositNamespaces: map + > allowedDepositNamespaces: map > } */ export interface TokenPool_DynamicConfig { @@ -360,7 +360,7 @@ export interface TokenPool_DynamicConfig { router: c.Address rateLimitAdmin: c.Address | null /* = null */ feeAdmin: c.Address | null - allowedDepositNamespaces: Map /* = [] as map */ + allowedDepositNamespaces: Set /* = [] as map */ } export const TokenPool_DynamicConfig = { @@ -368,7 +368,7 @@ export const TokenPool_DynamicConfig = { router: c.Address rateLimitAdmin?: c.Address | null /* = null */ feeAdmin: c.Address | null - allowedDepositNamespaces: Map /* = [] as map */ + allowedDepositNamespaces: Set /* = [] as map */ }): TokenPool_DynamicConfig { return { $: 'TokenPool_DynamicConfig', @@ -382,14 +382,23 @@ export const TokenPool_DynamicConfig = { router: s.loadAddress(), rateLimitAdmin: s.loadMaybeAddress(), feeAdmin: s.loadMaybeAddress(), - allowedDepositNamespaces: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool(), s)), + allowedDepositNamespaces: dictToSet(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), } }, store(self: TokenPool_DynamicConfig, b: c.Builder): void { b.storeAddress(self.router); b.storeAddress(self.rateLimitAdmin); b.storeAddress(self.feeAdmin); - b.storeDict(mapToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()), c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()); + b.storeDict(setToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); }, toCell(self: TokenPool_DynamicConfig): c.Cell { return makeCellFrom(self, TokenPool_DynamicConfig.store); @@ -397,108 +406,35 @@ export const TokenPool_DynamicConfig = { } /** - > struct TokenPool_MirroredPolicy { - > onRamps: map - > offRamps: map + > struct TokenPool_LocalPolicy { > cursedSubjects: CursedSubjects > } */ -export interface TokenPool_MirroredPolicy { - readonly $: 'TokenPool_MirroredPolicy' - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ +export interface TokenPool_LocalPolicy { + readonly $: 'TokenPool_LocalPolicy' cursedSubjects: CursedSubjects } -export const TokenPool_MirroredPolicy = { +export const TokenPool_LocalPolicy = { create(args: { - onRamps: Map /* = [] as map */ - offRamps: Map /* = [] as map */ cursedSubjects: CursedSubjects - }): TokenPool_MirroredPolicy { + }): TokenPool_LocalPolicy { return { - $: 'TokenPool_MirroredPolicy', + $: 'TokenPool_LocalPolicy', ...args } }, - fromSlice(s: c.Slice): TokenPool_MirroredPolicy { + fromSlice(s: c.Slice): TokenPool_LocalPolicy { return { - $: 'TokenPool_MirroredPolicy', - onRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), - offRamps: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ), s)), + $: 'TokenPool_LocalPolicy', cursedSubjects: CursedSubjects.fromSlice(s), } }, - store(self: TokenPool_MirroredPolicy, b: c.Builder): void { - b.storeDict(mapToDict(self.onRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); - b.storeDict(mapToDict(self.offRamps, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - )); + store(self: TokenPool_LocalPolicy, b: c.Builder): void { CursedSubjects.store(self.cursedSubjects, b); }, - toCell(self: TokenPool_MirroredPolicy): c.Cell { - return makeCellFrom(self, TokenPool_MirroredPolicy.store); - } -} - -/** - > struct TokenPool_RampUpdate { - > remoteChainSelector: uint64 - > onRamp: address? - > offRamp: address? - > } - */ -export interface TokenPool_RampUpdate { - readonly $: 'TokenPool_RampUpdate' - remoteChainSelector: uint64 - onRamp: c.Address | null /* = null */ - offRamp: c.Address | null /* = null */ -} - -export const TokenPool_RampUpdate = { - create(args: { - remoteChainSelector: uint64 - onRamp?: c.Address | null /* = null */ - offRamp?: c.Address | null /* = null */ - }): TokenPool_RampUpdate { - return { - $: 'TokenPool_RampUpdate', - onRamp: null, - offRamp: null, - ...args - } - }, - fromSlice(s: c.Slice): TokenPool_RampUpdate { - return { - $: 'TokenPool_RampUpdate', - remoteChainSelector: s.loadUintBig(64), - onRamp: s.loadMaybeAddress(), - offRamp: s.loadMaybeAddress(), - } - }, - store(self: TokenPool_RampUpdate, b: c.Builder): void { - b.storeUint(self.remoteChainSelector, 64); - b.storeAddress(self.onRamp); - b.storeAddress(self.offRamp); - }, - toCell(self: TokenPool_RampUpdate): c.Cell { - return makeCellFrom(self, TokenPool_RampUpdate.store); + toCell(self: TokenPool_LocalPolicy): c.Cell { + return makeCellFrom(self, TokenPool_LocalPolicy.store); } } @@ -1236,7 +1172,7 @@ export const TokenPool_ReleaseOrMintOutV1 = { /** > struct TokenPool_AdminConfig { > ownable: Cell - > rmnProxy: address + > rmnProxy: address? > dynamicConfig: Cell > jettonClient: JettonClient > allowedFinalityConfig: uint32 @@ -1247,7 +1183,7 @@ export const TokenPool_ReleaseOrMintOutV1 = { export interface TokenPool_AdminConfig { readonly $: 'TokenPool_AdminConfig' ownable: Ownable2Step - rmnProxy: c.Address + rmnProxy: c.Address | null dynamicConfig: TokenPool_DynamicConfig jettonClient: JettonClient allowedFinalityConfig: uint32 /* = 0 as uint32 */ @@ -1258,7 +1194,7 @@ export interface TokenPool_AdminConfig { export const TokenPool_AdminConfig = { create(args: { ownable: Ownable2Step - rmnProxy: c.Address + rmnProxy: c.Address | null dynamicConfig: TokenPool_DynamicConfig jettonClient: JettonClient allowedFinalityConfig?: uint32 /* = 0 as uint32 */ @@ -1277,7 +1213,7 @@ export const TokenPool_AdminConfig = { return { $: 'TokenPool_AdminConfig', ownable: loadCellRef(s, Ownable2Step.fromSlice), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), dynamicConfig: loadCellRef(s, TokenPool_DynamicConfig.fromSlice), jettonClient: JettonClient.fromSlice(s), allowedFinalityConfig: s.loadUintBig(32), @@ -1304,7 +1240,7 @@ export const TokenPool_AdminConfig = { /** > struct TokenPool_Data { > adminConfig: Cell - > mirroredPolicy: Cell + > localPolicy: Cell > tokenDecimals: uint8 > remoteChainConfigs: map > tokenTransferFeeConfigs: map @@ -1313,7 +1249,7 @@ export const TokenPool_AdminConfig = { export interface TokenPool_Data { readonly $: 'TokenPool_Data' adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -1322,7 +1258,7 @@ export interface TokenPool_Data { export const TokenPool_Data = { create(args: { adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -1336,7 +1272,7 @@ export const TokenPool_Data = { return { $: 'TokenPool_Data', adminConfig: loadCellRef(s, TokenPool_AdminConfig.fromSlice), - mirroredPolicy: loadCellRef(s, TokenPool_MirroredPolicy.fromSlice), + localPolicy: loadCellRef(s, TokenPool_LocalPolicy.fromSlice), tokenDecimals: s.loadUintBig(8), remoteChainConfigs: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store), s)), tokenTransferFeeConfigs: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store), s)), @@ -1344,7 +1280,7 @@ export const TokenPool_Data = { }, store(self: TokenPool_Data, b: c.Builder): void { storeCellRef(self.adminConfig, b, TokenPool_AdminConfig.store); - storeCellRef(self.mirroredPolicy, b, TokenPool_MirroredPolicy.store); + storeCellRef(self.localPolicy, b, TokenPool_LocalPolicy.store); b.storeUint(self.tokenDecimals, 8); b.storeDict(mapToDict(self.remoteChainConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store)), c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_RemoteChainConfig.fromSlice, TokenPool_RemoteChainConfig.store)); b.storeDict(mapToDict(self.tokenTransferFeeConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store)), c.Dictionary.Keys.BigUint(64), createDictionaryValue(TokenPool_TokenTransferFeeConfig.fromSlice, TokenPool_TokenTransferFeeConfig.store)); @@ -1732,13 +1668,13 @@ export const TokenPool_DeployableCodeSet = { /** > struct (0x84384142) TokenPool_SetAllowedDepositNamespaces { > queryId: uint64 - > allowedDepositNamespaces: map + > allowedDepositNamespaces: map > } */ export interface TokenPool_SetAllowedDepositNamespaces { readonly $: 'TokenPool_SetAllowedDepositNamespaces' queryId: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set } export const TokenPool_SetAllowedDepositNamespaces = { @@ -1746,7 +1682,7 @@ export const TokenPool_SetAllowedDepositNamespaces = { create(args: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }): TokenPool_SetAllowedDepositNamespaces { return { $: 'TokenPool_SetAllowedDepositNamespaces', @@ -1759,13 +1695,22 @@ export const TokenPool_SetAllowedDepositNamespaces = { return { $: 'TokenPool_SetAllowedDepositNamespaces', queryId: s.loadUintBig(64), - allowedDepositNamespaces: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool(), s)), + allowedDepositNamespaces: dictToSet(c.Dictionary.load(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ), s)), } }, store(self: TokenPool_SetAllowedDepositNamespaces, b: c.Builder): void { b.storeUint(0x84384142, 32); b.storeUint(self.queryId, 64); - b.storeDict(mapToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()), c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool()); + b.storeDict(setToDict(self.allowedDepositNamespaces, c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )), c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + )); }, toCell(self: TokenPool_SetAllowedDepositNamespaces): c.Cell { return makeCellFrom(self, TokenPool_SetAllowedDepositNamespaces.store); @@ -1901,59 +1846,16 @@ export const TokenPool_ApplyTokenTransferFeeConfigUpdates = { } } -/** - > struct (0xe30764be) TokenPool_UpdateRampAccess { - > queryId: uint64 - > updates: SnakedCell - > } - */ -export interface TokenPool_UpdateRampAccess { - readonly $: 'TokenPool_UpdateRampAccess' - queryId: uint64 - updates: SnakedCell -} - -export const TokenPool_UpdateRampAccess = { - PREFIX: 0xe30764be, - - create(args: { - queryId?: uint64 - updates: SnakedCell - }): TokenPool_UpdateRampAccess { - return { - $: 'TokenPool_UpdateRampAccess', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_UpdateRampAccess { - loadAndCheckPrefix32(s, 0xe30764be, 'TokenPool_UpdateRampAccess'); - return { - $: 'TokenPool_UpdateRampAccess', - queryId: s.loadUintBig(64), - updates: loadSnakedCellOf(s, TokenPool_RampUpdate.fromSlice), - } - }, - store(self: TokenPool_UpdateRampAccess, b: c.Builder): void { - b.storeUint(0xe30764be, 32); - b.storeUint(self.queryId, 64); - storeSnakedCellOf(self.updates, b, TokenPool_RampUpdate.store); - }, - toCell(self: TokenPool_UpdateRampAccess): c.Cell { - return makeCellFrom(self, TokenPool_UpdateRampAccess.store); - } -} - /** > struct (0x9929b642) TokenPool_SetRMNProxy { > queryId: uint64 - > rmnProxy: address + > rmnProxy: address? > } */ export interface TokenPool_SetRMNProxy { readonly $: 'TokenPool_SetRMNProxy' queryId: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null } export const TokenPool_SetRMNProxy = { @@ -1961,7 +1863,7 @@ export const TokenPool_SetRMNProxy = { create(args: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }): TokenPool_SetRMNProxy { return { $: 'TokenPool_SetRMNProxy', @@ -1974,7 +1876,7 @@ export const TokenPool_SetRMNProxy = { return { $: 'TokenPool_SetRMNProxy', queryId: s.loadUintBig(64), - rmnProxy: s.loadAddress(), + rmnProxy: s.loadMaybeAddress(), } }, store(self: TokenPool_SetRMNProxy, b: c.Builder): void { @@ -3352,44 +3254,6 @@ export const TokenPool_ChainUpdatesApplied = { } } -/** - > struct (0xd7f5c563) TokenPool_RampAccessUpdatesApplied { - > queryId: uint64 - > } - */ -export interface TokenPool_RampAccessUpdatesApplied { - readonly $: 'TokenPool_RampAccessUpdatesApplied' - queryId: uint64 -} - -export const TokenPool_RampAccessUpdatesApplied = { - PREFIX: 0xd7f5c563, - - create(args: { - queryId?: uint64 - }): TokenPool_RampAccessUpdatesApplied { - return { - $: 'TokenPool_RampAccessUpdatesApplied', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): TokenPool_RampAccessUpdatesApplied { - loadAndCheckPrefix32(s, 0xd7f5c563, 'TokenPool_RampAccessUpdatesApplied'); - return { - $: 'TokenPool_RampAccessUpdatesApplied', - queryId: s.loadUintBig(64), - } - }, - store(self: TokenPool_RampAccessUpdatesApplied, b: c.Builder): void { - b.storeUint(0xd7f5c563, 32); - b.storeUint(self.queryId, 64); - }, - toCell(self: TokenPool_RampAccessUpdatesApplied): c.Cell { - return makeCellFrom(self, TokenPool_RampAccessUpdatesApplied.store); - } -} - /** > struct (0x28cbcc64) TokenPool_FeeConfigApplied { > queryId: uint64 @@ -3904,51 +3768,6 @@ export const TokenPool_RateLimitConfigured = { } } -/** - > struct TokenPool_RampAccessUpdated { - > remoteChainSelector: uint64 - > onRamp: address? - > offRamp: address? - > } - */ -export interface TokenPool_RampAccessUpdated { - readonly $: 'TokenPool_RampAccessUpdated' - remoteChainSelector: uint64 - onRamp: c.Address | null /* = null */ - offRamp: c.Address | null /* = null */ -} - -export const TokenPool_RampAccessUpdated = { - create(args: { - remoteChainSelector: uint64 - onRamp?: c.Address | null /* = null */ - offRamp?: c.Address | null /* = null */ - }): TokenPool_RampAccessUpdated { - return { - $: 'TokenPool_RampAccessUpdated', - onRamp: null, - offRamp: null, - ...args - } - }, - fromSlice(s: c.Slice): TokenPool_RampAccessUpdated { - return { - $: 'TokenPool_RampAccessUpdated', - remoteChainSelector: s.loadUintBig(64), - onRamp: s.loadMaybeAddress(), - offRamp: s.loadMaybeAddress(), - } - }, - store(self: TokenPool_RampAccessUpdated, b: c.Builder): void { - b.storeUint(self.remoteChainSelector, 64); - b.storeAddress(self.onRamp); - b.storeAddress(self.offRamp); - }, - toCell(self: TokenPool_RampAccessUpdated): c.Cell { - return makeCellFrom(self, TokenPool_RampAccessUpdated.store); - } -} - /** > type SnakedCell = cell */ @@ -4479,7 +4298,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class TokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('te6ccgECOgEAAY4AART/APSkE/S88sgLAQIBYgIDABTQMPiR8kCED/LwAgEgBAUCASAGBwIBIC4vAgEgCAkCASAaGwIBIAoLAgEgFBUCASAMDQIBSBITAgFmDg8CAUgQEQALoF4QP8vCAAuhxhA/y8IAC6V1CB/l4QALpyMIH+XhAAypX4QP8vAADKrQhA/y8AIBbhYXAgEgGBkAC6dRCB/l4QALpFEIH+XhAA2sPkIH+XhAAA2sgMIH+XhAAgEgHB0CASAoKQIBIB4fAgEgJicCASAgIQIBYiQlAAyqh4QP8vACASAiIwALpjsIH+XhAAulCwgf5eEAC6H6ED/LwgALoH4QP8vCAA2vRUIH+XhAAA2s40IH+XhAAgFYKisCAnUsLQAMqXqED/LwAAyqtoQP8vAAC6OuED/LwgALohoQP8vCAgEgMDECASAyMwANtH2wgf5eEAANt9zwgf5eEAANtI2wgf5eEAIBIDQ1AgFqNjcCASA4OQALpV8IH+XhAAunAwgf5eEADaxrwgf5eEAADaw4wgf5eEA='); + static CodeCell = c.Cell.fromBase64('te6ccgECNAEAAWgAART/APSkE/S88sgLAQIBYgIDABTQMPiR8kCED/LwAgEgBAUCASAGBwIBICgpAgEgCAkCASAWFwIBIAoLAgEgEhMCASAMDQIBSBARAgFmDg8ADa3Iwgf5eEAAC6BeED/LwgALocYQP8vCAAypX4QP8vAADKrQhA/y8AIBbhQVAA2wHyED/LwgAAunUQgf5eEAC6RRCB/l4QIBIBgZAgEgJCUCASAaGwIBICIjAgEgHB0CAWIgIQAMqoeED/LwAgEgHh8AC6Y7CB/l4QALpQsIH+XhAAuh+hA/y8IAC6B+ED/LwgANr0VCB/l4QAANrONCB/l4QAIBWCYnAA2yuuED/LwgAAypeoQP8vAADKq2hA/y8AIBICorAgEgLC0ADbR9sIH+XhAADbfc8IH+XhAADbSNsIH+XhACASAuLwIBajAxAgEgMjMAC6VfCB/l4QALpwMIH+XhAA2sa8IH+XhAAA2sOMIH+XhA'); static Errors = { } @@ -4509,7 +4328,7 @@ export class TokenPool implements c.Contract { static fromStorage(emptyStorage: { adminConfig: TokenPool_AdminConfig - mirroredPolicy: TokenPool_MirroredPolicy + localPolicy: TokenPool_LocalPolicy tokenDecimals: uint8 remoteChainConfigs: Map /* = [] as map */ tokenTransferFeeConfigs: Map /* = [] as map */ @@ -4669,7 +4488,7 @@ export class TokenPool implements c.Contract { static createCellOfTokenPoolSetAllowedDepositNamespaces(body: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }) { return TokenPool_SetAllowedDepositNamespaces.toCell(TokenPool_SetAllowedDepositNamespaces.create(body)); } @@ -4689,16 +4508,9 @@ export class TokenPool implements c.Contract { return TokenPool_ApplyTokenTransferFeeConfigUpdates.toCell(TokenPool_ApplyTokenTransferFeeConfigUpdates.create(body)); } - static createCellOfTokenPoolUpdateRampAccess(body: { - queryId?: uint64 - updates: SnakedCell - }) { - return TokenPool_UpdateRampAccess.toCell(TokenPool_UpdateRampAccess.create(body)); - } - static createCellOfTokenPoolSetRMNProxy(body: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }) { return TokenPool_SetRMNProxy.toCell(TokenPool_SetRMNProxy.create(body)); } @@ -4948,7 +4760,7 @@ export class TokenPool implements c.Contract { async sendTokenPoolSetAllowedDepositNamespaces(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - allowedDepositNamespaces: Map + allowedDepositNamespaces: Set }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -4980,20 +4792,9 @@ export class TokenPool implements c.Contract { }); } - async sendTokenPoolUpdateRampAccess(provider: ContractProvider, via: Sender, msgValue: coins, body: { - queryId?: uint64 - updates: SnakedCell - }, extraOptions?: ExtraSendOptions) { - return provider.internal(via, { - value: msgValue, - body: TokenPool_UpdateRampAccess.toCell(TokenPool_UpdateRampAccess.create(body)), - ...extraOptions - }); - } - async sendTokenPoolSetRMNProxy(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - rmnProxy: c.Address + rmnProxy: c.Address | null }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -5064,29 +4865,13 @@ export class TokenPool implements c.Contract { return r.readBoolean(); } - async getOnRamp(provider: ContractProvider, remoteChainSelector: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('onRamp', [ - { type: 'int', value: remoteChainSelector }, - ])); - return r.readNullable( - (r) => r.readSlice().loadAddress() - ); - } - - async getOffRamp(provider: ContractProvider, remoteChainSelector: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('offRamp', [ - { type: 'int', value: remoteChainSelector }, - ])); + async getRMNProxy(provider: ContractProvider): Promise { + const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); return r.readNullable( (r) => r.readSlice().loadAddress() ); } - async getRMNProxy(provider: ContractProvider): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); - return r.readSlice().loadAddress(); - } - async getVerifyNotCursed(provider: ContractProvider, subject: uint128): Promise { const r = StackReader.fromGetMethod(1, await provider.get('verifyNotCursed', [ { type: 'int', value: subject }, @@ -5124,7 +4909,10 @@ export class TokenPool implements c.Contract { feeAdmin: r.readNullable( (r) => r.readSlice().loadAddress() ), - allowedDepositNamespaces: dictToMap(r.readDictionary(c.Dictionary.Keys.BigUint(32), c.Dictionary.Values.Bool())), + allowedDepositNamespaces: dictToSet(r.readDictionary(c.Dictionary.Keys.BigUint(32), createDictionaryValue<[]>( + (s) => [], + (v,b) => { {} } + ))), }); } @@ -5206,7 +4994,9 @@ export class TokenPool implements c.Contract { return ({ $: 'TokenPool_AdminConfig', ownable: r.readCellRef(Ownable2Step.fromSlice), - rmnProxy: r.readSlice().loadAddress(), + rmnProxy: r.readNullable( + (r) => r.readSlice().loadAddress() + ), dynamicConfig: r.readCellRef(TokenPool_DynamicConfig.fromSlice), jettonClient: ({ $: 'JettonClient', @@ -5223,28 +5013,6 @@ export class TokenPool implements c.Contract { }); } - async getMirroredPolicy(provider: ContractProvider): Promise { - const r = StackReader.fromGetMethod(3, await provider.get('getMirroredPolicy', [])); - return ({ - $: 'TokenPool_MirroredPolicy', - onRamps: dictToMap(r.readDictionary(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ))), - offRamps: dictToMap(r.readDictionary(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => s.loadAddress(), - (v,b) => b.storeAddress(v) - ))), - cursedSubjects: ({ - $: 'CursedSubjects', - data: dictToSet(r.readDictionary(c.Dictionary.Keys.BigUint(128), createDictionaryValue<[]>( - (s) => [], - (v,b) => { {} } - ))), - }), - }); - } - async getRemoteChainConfig(provider: ContractProvider, remoteChainSelector: uint64): Promise { const r = StackReader.fromGetMethod(5, await provider.get('getRemoteChainConfig', [ { type: 'int', value: remoteChainSelector }, diff --git a/deployment/ccip/1_6_0/sequences/tokens.go b/deployment/ccip/1_6_0/sequences/tokens.go index f80d3bab6..5977ec54a 100644 --- a/deployment/ccip/1_6_0/sequences/tokens.go +++ b/deployment/ccip/1_6_0/sequences/tokens.go @@ -399,10 +399,12 @@ func (a *TonTokenAdapter) DeployTokenPoolForToken() *cldf_ops.Sequence[tokensapi }, RMNProxy: owner, DynamicConfig: tokenpool.DynamicConfig{ - Router: &routerAddr, - RateLimitAdmin: rateLimitAdmin, - FeeAdmin: feeAdmin, - AllowedDepositNamespaces: tlbe.NewEmptyDict[uint32, bool](), + Router: &routerAddr, + RateLimitAdmin: rateLimitAdmin, + FeeAdmin: feeAdmin, + // Unit-value set (map); an empty dict serializes as an + // empty map. Must be non-nil: the tlb:"." tag errors on a nil dict. + AllowedDepositNamespaces: tlbe.NewEmptyDict[uint32, struct{}](), }, JettonClient: tokenpool.JettonClient{ MasterAddress: tokenAddr, @@ -411,12 +413,13 @@ func (a *TonTokenAdapter) DeployTokenPoolForToken() *cldf_ops.Sequence[tokensapi AllowedFinalityConfig: allowedFinality, AdvancedPoolHooks: nil, }, - MirroredPolicy: tokenpool.MirroredPolicy{ - // nil dicts serialize as an empty map (a single "no entries" bit), matching - // the Tolk contract's createEmptyMap() default. - OnRamps: nil, - OffRamps: nil, - CursedSubjects: tokenpool.CursedSubjects{Data: nil}, + LocalPolicy: tokenpool.LocalPolicy{ + // An empty dict serializes as an empty map (a single "no entries" + // bit), matching the Tolk contract's createEmptyMap() default. + // Must be non-nil: the tlb:"." tag errors on a nil dict. + CursedSubjects: tokenpool.CursedSubjects{ + Data: tlbe.NewEmptyDict[tlbe.Uint128, struct{}](), + }, }, TokenDecimals: defaultJettonDecimals, RemoteChainConfigs: nil, @@ -510,9 +513,9 @@ func (a *TonTokenAdapter) ConfigureTokenForTransfersSequence() *cldf_ops.Sequenc return sequences.OnChainOutput{}, fmt.Errorf("retrieve Deployable code for TokenAdminRegistry entry: %w", err) } - // The OffRamp always comes from the datastore: it is the contract that sends - // TokenPool_ReleaseOrMint to the pool, so it must be registered as the pool's - // trusted offRamp (see applyRampAccessUpdates below). + // The OffRamp is registered in the Router's offRamps map by the lane's + // ApplyRampUpdates sequence; the Router forwards ReleaseOrMint to the pool, + // so the pool itself no longer tracks per-lane ramps. stateCCIP, loadErr := tonstate.LoadCCIPOnChainStateUsingDataStore(input.ExistingDataStore, input.ChainSelector) if loadErr != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to load TON CCIP state for chain %d: %w", input.ChainSelector, loadErr) @@ -529,14 +532,6 @@ func (a *TonTokenAdapter) ConfigureTokenForTransfersSequence() *cldf_ops.Sequenc registryAddr = &r } - var offRampAddr *address.Address - if !stateCCIP.OffRamp.IsAddrNone() { - o := stateCCIP.OffRamp - offRampAddr = &o - } else if len(input.RemoteChains) > 0 { - return sequences.OnChainOutput{}, fmt.Errorf("no OffRamp address found in the datastore for chain %d: the token pool would reject inbound ReleaseOrMint with TokenPool_Error.Unauthorized", input.ChainSelector) - } - body := codec.MustWrapMessage[any](bindings.TypeTokenAdminRegistry, tokenadminregistry.RegisterToken{ TokenAddress: tokenAddr, TokenInfo: tokenadminregistryentry.TokenInfo{ @@ -593,14 +588,6 @@ func (a *TonTokenAdapter) ConfigureTokenForTransfersSequence() *cldf_ops.Sequenc return sequences.OnChainOutput{}, fmt.Errorf("failed to configure remote chains on token pool at %s: %w", poolAddr.String(), err) } - // Register the Router as the pool's trusted onRamp and the OffRamp as its trusted - // offRamp for every remote chain being wired up. - // TODO This should be changed in the contracts flow so that the onramp is the one calling instead of the Router - routerAddr := stateCCIP.Router - if err := applyRampAccessUpdates(b, dp, poolAddr, &routerAddr, offRampAddr, input.RemoteChains); err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to configure ramp access on token pool at %s: %w", poolAddr.String(), err) - } - return sequences.OnChainOutput{}, nil }, ) @@ -706,55 +693,6 @@ func applyRemoteChainUpdates( return err } -// applyRampAccessUpdates registers onRamp/offRamp as the trusted local ramps for every -// remote chain in remoteChains, via TokenPool_UpdateRampAccess. It is a no-op when no -// remote chains are provided. Without this, the pool's mirroredPolicy.onRamps/offRamps -// maps stay empty and TokenPool.ensureOutboundAccess/ensureInboundAccess (entrypoint.tolk) -// reject LockOrBurn/ReleaseOrMint with TokenPool_Error.Unauthorized. -func applyRampAccessUpdates( - b cldf_ops.Bundle, - dp *dep.DependencyProvider, - poolAddr *address.Address, - onRamp *address.Address, - offRamp *address.Address, - remoteChains map[uint64]tokensapi.RemoteChainConfig[[]byte, string], -) error { - if len(remoteChains) == 0 { - return nil - } - - updates := make(common.SnakedCell[tokenpool.RampUpdate], 0, len(remoteChains)) - for remoteSelector := range remoteChains { - updates = append(updates, tokenpool.RampUpdate{ - RemoteChainSelector: remoteSelector, - OnRamp: onRamp, - OffRamp: offRamp, - }) - } - - queryID, err := ton_tvm.RandomQueryID() - if err != nil { - return fmt.Errorf("failed to generate query id for ramp access update: %w", err) - } - - body := codec.MustWrapMessage[any](bindings.TypeLockReleaseTokenPool, tokenpool.UpdateRampAccess{ - QueryID: queryID, - Updates: updates, - }) - - _, err = cldf_ops.ExecuteOperation(b, opston.SendMessages, dp, opston.SendMessagesInput{ - Messages: []opston.InternalMessage[any]{ - { - Bounce: true, - DstAddr: poolAddr, - Amount: tlb.MustFromTON("0.1"), - Body: body, - }, - }, - }) - return err -} - // disabledRateLimitConfigPair returns a fully-disabled rate limiter pair. func disabledRateLimitConfigPair() tokenpool.RateLimitConfigPair { disabled := tokenpool.RateLimitConfig{ diff --git a/deployment/go.mod b/deployment/go.mod index 9b34e084e..8723bac22 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -4,6 +4,9 @@ go 1.26.6 replace github.com/smartcontractkit/chainlink-ton => ../ +// TMP: remove in follow up PR +replace github.com/smartcontractkit/chainlink-ton/cciplib => ../cciplib + require ( github.com/Masterminds/semver/v3 v3.5.0 github.com/gagliardetto/binary v0.8.0 diff --git a/deployment/go.sum b/deployment/go.sum index 6bcc01db9..1cd951095 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -692,8 +692,6 @@ github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.6 h1:2cW github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.6/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5/go.mod h1:kHYJnZUqiPF7/xN5273prV+srrLJkS77GbBXHLKQpx0= -github.com/smartcontractkit/chainlink-ton/cciplib v0.1.1-0.20260917154632-2b04f920494a h1:TpYTnFTO3JZlhDeyn4R45EFGzeBCaWiEYeOtskzF3aQ= -github.com/smartcontractkit/chainlink-ton/cciplib v0.1.1-0.20260917154632-2b04f920494a/go.mod h1:SOshBOF1ZxrLJlqtcJv9qpg/8soV6fCFqU5xg+imXZI= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9 h1:7Ut0g+Pdm+gcu2J/Xv8OpQOVf7uLGErMX8yhC4b4tIA= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9/go.mod h1:h9hMs6K4hT1+mjYnJD3/SW1o7yC/sKjNi0Qh8hLfiCE= github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20250422175525-b7575d96bd4d h1:qLmSOOtB/Ogn79eIDkuujOu8M5Jd747V1H7Brk/nTvo= diff --git a/devenv/go.mod b/devenv/go.mod index d1805dc44..f3e7aba41 100644 --- a/devenv/go.mod +++ b/devenv/go.mod @@ -6,6 +6,9 @@ replace ( github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20250528121202-292529af39df github.com/smartcontractkit/chainlink-ton => ../ + + // TMP: remove in follow up PR + github.com/smartcontractkit/chainlink-ton/cciplib => ../cciplib github.com/smartcontractkit/chainlink-ton/deployment => ../deployment ) diff --git a/devenv/go.sum b/devenv/go.sum index dc99c5aed..d9d8fa288 100644 --- a/devenv/go.sum +++ b/devenv/go.sum @@ -686,8 +686,6 @@ github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.6 h1:2cW github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.6/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5/go.mod h1:kHYJnZUqiPF7/xN5273prV+srrLJkS77GbBXHLKQpx0= -github.com/smartcontractkit/chainlink-ton/cciplib v0.1.1-0.20260917154632-2b04f920494a h1:TpYTnFTO3JZlhDeyn4R45EFGzeBCaWiEYeOtskzF3aQ= -github.com/smartcontractkit/chainlink-ton/cciplib v0.1.1-0.20260917154632-2b04f920494a/go.mod h1:SOshBOF1ZxrLJlqtcJv9qpg/8soV6fCFqU5xg+imXZI= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9 h1:7Ut0g+Pdm+gcu2J/Xv8OpQOVf7uLGErMX8yhC4b4tIA= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9/go.mod h1:h9hMs6K4hT1+mjYnJD3/SW1o7yC/sKjNi0Qh8hLfiCE= github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20250528121202-292529af39df h1:36e3ROIZyV/qE8SvFOACXtXfMOMd9vG4+zY2v2ScXkI= diff --git a/docs/contracts/overview/ccip/token-pools/IMPLEMENTATION.md b/docs/contracts/overview/ccip/token-pools/IMPLEMENTATION.md index 45de0d1f2..3921680c9 100644 --- a/docs/contracts/overview/ccip/token-pools/IMPLEMENTATION.md +++ b/docs/contracts/overview/ccip/token-pools/IMPLEMENTATION.md @@ -48,11 +48,15 @@ contract Storage ──load──▶ TokenPool{ data, context: Storage ### 3.1 Deliberate divergences from EVM (correct for TVM) -1. **Mirrored policy, not synchronous calls.** EVM calls `Router`/`RMN` inline. - TON cannot return synchronously, so the pool keeps **local mirrors** of - onRamp/offRamp authorization and RMN cursed-subjects, pushed asynchronously by - trusted senders (analogous to `OffRamp_UpdateCursedSubjects`). Read on the hot - path; never queried live. (`TokenPool_MirroredPolicy` in `types.tolk`.) +1. **Router chokepoint, local RMN policy.** EVM calls `Router`/`RMN` inline; on TON + the `Router` is the pool's **sole** entrypoint (`TokenPool.onlyRouter`) for both + directions — outbound `LockOrBurn` and inbound `ReleaseOrMint` (the latter relayed + by the Router via `RelayReleaseOrMint`, which also routes pool rejections to the + OffRamp's failure rail). This keeps pool decommission O(1): revoking the Router's + deployable code disables every pool without touching the OffRamp's allowed list. + The pool keeps only a **local** mirror of RMN cursed-subjects, pushed asynchronously + by the RMN proxy (`TokenPool_LocalPolicy` in `types.tolk`). Read on the hot path; + never queried live. 2. **Per-operation async state machines.** Each cross-chain op is keyed by `queryId` in a pending map; the pool replies only after positive confirmation (`ReturnExcessesBack` / lockbox callbacks). "Confirmation-before-reply." diff --git a/go.mod b/go.mod index 784ac9262..84687e773 100644 --- a/go.mod +++ b/go.mod @@ -148,6 +148,9 @@ require ( replace github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20250422175525-b7575d96bd4d +// TMP: remove in follow up PR +replace github.com/smartcontractkit/chainlink-ton/cciplib => ./cciplib + tool ( github.com/jmank88/gomods github.com/jmank88/modgraph diff --git a/go.sum b/go.sum index fd23c596d..f9952e628 100644 --- a/go.sum +++ b/go.sum @@ -276,8 +276,6 @@ github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260710151514-2 github.com/smartcontractkit/chainlink-protos/metering/go v0.0.0-20260710151514-27b5a126dabe/go.mod h1:z7lx7wI3XZ4u9kmUtAVdwn1BCC9T8aieWSDcuDgPTdQ= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16 h1:/vkKPJoweLkRd56V4YHGRAtTG4+/JAlgklGEfvH6l4c= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260709145319-7782fb89eb16/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= -github.com/smartcontractkit/chainlink-ton/cciplib v0.1.1-0.20260917154632-2b04f920494a h1:TpYTnFTO3JZlhDeyn4R45EFGzeBCaWiEYeOtskzF3aQ= -github.com/smartcontractkit/chainlink-ton/cciplib v0.1.1-0.20260917154632-2b04f920494a/go.mod h1:SOshBOF1ZxrLJlqtcJv9qpg/8soV6fCFqU5xg+imXZI= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad/go.mod h1:T4zH9R8R8lVWKfU7tUvYz2o2jMv1OpGCdpY2j2QZXzU= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= diff --git a/pkg/bindings/lib/access/rbac/types.go b/pkg/bindings/lib/access/rbac/types.go index 7e1646a28..fca8bbbb4 100644 --- a/pkg/bindings/lib/access/rbac/types.go +++ b/pkg/bindings/lib/access/rbac/types.go @@ -136,7 +136,7 @@ var TLBs = tvm.MustNewTLBMap([]any{ // AccessControl data struct, auto-serialized to/from cell. type Data struct { // Roles mapping - Roles *tlbe.Dict[*tlbe.Uint256, RoleData] `tlb:"."` + Roles *tlbe.Dict[tlbe.Uint256, RoleData] `tlb:"."` } // Internal storage struct for role data diff --git a/pkg/bindings/mcms/mcms/storage.go b/pkg/bindings/mcms/mcms/storage.go index 0ab24cde6..1ddf22c52 100644 --- a/pkg/bindings/mcms/mcms/storage.go +++ b/pkg/bindings/mcms/mcms/storage.go @@ -18,13 +18,13 @@ func EmptyDataFrom(id uint32, owner *address.Address, chainID int64) Data { PendingOwner: nil, }, Oracle: owner, - Signers: tlbe.NewEmptyDict[*tlbe.Uint160, Signer](), + Signers: tlbe.NewEmptyDict[tlbe.Uint160, Signer](), Config: Config{ Signers: tlbe.NewEmptyDict[uint8, Signer](), GroupQuorums: tlbe.NewEmptyDict[uint8, uint8](), GroupParents: tlbe.NewEmptyDict[uint8, uint8](), }, - SeenSignedHashes: tlbe.NewEmptyDict[*tlbe.Uint256, bool](), + SeenSignedHashes: tlbe.NewEmptyDict[tlbe.Uint256, bool](), RootInfo: RootInfo{ ExpiringRootAndOpCount: ExpiringRootAndOpCount{ Root: tlbe.NewUint256(big.NewInt(0)), diff --git a/pkg/bindings/mcms/mcms/types.go b/pkg/bindings/mcms/mcms/types.go index a74161ca5..d6c22bcdd 100644 --- a/pkg/bindings/mcms/mcms/types.go +++ b/pkg/bindings/mcms/mcms/types.go @@ -273,13 +273,13 @@ type Data struct { // Signers is used to easily validate the existence of the signer by its public key. We still // have signers stored in config in order to easily deactivate them when a new config is set. - Signers *tlbe.Dict[*tlbe.Uint160, Signer] `tlb:"."` // exists if the public key is a signer + Signers *tlbe.Dict[tlbe.Uint160, Signer] `tlb:"."` // exists if the public key is a signer // The current configuration of the contract Config Config `tlb:"^"` // @dev split out as cell to avoid size limits // Remember signedHashes that this contract has seen. Each signedHash can only be set once. - SeenSignedHashes *tlbe.Dict[*tlbe.Uint256, bool] `tlb:"."` + SeenSignedHashes *tlbe.Dict[tlbe.Uint256, bool] `tlb:"."` // The current RootMetadata and ExpiringRootAndOpCount wrapped in a cell bc size limits. RootInfo RootInfo `tlb:"^"` diff --git a/pkg/bindings/mcms/timelock/storage.go b/pkg/bindings/mcms/timelock/storage.go index 09117e2de..936c5db08 100644 --- a/pkg/bindings/mcms/timelock/storage.go +++ b/pkg/bindings/mcms/timelock/storage.go @@ -11,7 +11,7 @@ func EmptyDataFrom(id uint32) Data { return Data{ ID: id, MinDelay: 0, - Timestamps: tlbe.NewEmptyDict[*tlbe.Uint256, uint64](), + Timestamps: tlbe.NewEmptyDict[tlbe.Uint256, uint64](), BlockedFnSelectorsLen: 0, BlockedFnSelectors: tlbe.NewEmptyDict[uint32, bool](), ExecutorRoleCheckEnabled: true, @@ -19,10 +19,10 @@ func EmptyDataFrom(id uint32) Data { ValidAfter: 0, OpFinalizationTimeout: 0, OpPendingID: tlbe.NewUint256(big.NewInt(0)), - OpPendingCalls: tlbe.NewEmptyDict[*tlbe.Uint256, bool](), + OpPendingCalls: tlbe.NewEmptyDict[tlbe.Uint256, bool](), }, RBAC: rbac.Data{ - Roles: tlbe.NewEmptyDict[*tlbe.Uint256, rbac.RoleData](), + Roles: tlbe.NewEmptyDict[tlbe.Uint256, rbac.RoleData](), }, } } diff --git a/pkg/bindings/mcms/timelock/types.go b/pkg/bindings/mcms/timelock/types.go index 8a0eebbcf..954f0e4ec 100644 --- a/pkg/bindings/mcms/timelock/types.go +++ b/pkg/bindings/mcms/timelock/types.go @@ -347,7 +347,7 @@ type Data struct { // Minimum delay for operations in seconds MinDelay uint32 `tlb:"## 32"` // Map of operation id to timestamp - Timestamps *tlbe.Dict[*tlbe.Uint256, uint64] `tlb:"."` + Timestamps *tlbe.Dict[tlbe.Uint256, uint64] `tlb:"."` // Number of fn selectors blocked by the contract. BlockedFnSelectorsLen uint32 `tlb:"## 32"` @@ -397,7 +397,7 @@ type OpPendingInfo struct { // The id of the currently pending operation (OperationBatch hash) OpPendingID *tlbe.Uint256 `tlb:"."` // The ids (fingerprints) for calls awaiting finalization in the pending op (true = pending, false = finalized/bounced) - OpPendingCalls *tlbe.Dict[*tlbe.Uint256, bool] `tlb:"."` + OpPendingCalls *tlbe.Dict[tlbe.Uint256, bool] `tlb:"."` } // --- Constants --- diff --git a/pkg/ccip/bindings/tokenpool/burnmint/reader.go b/pkg/ccip/bindings/tokenpool/burnmint/reader.go index 07b1790c9..822977b5a 100644 --- a/pkg/ccip/bindings/tokenpool/burnmint/reader.go +++ b/pkg/ccip/bindings/tokenpool/burnmint/reader.go @@ -20,8 +20,6 @@ var ( GetTokenDecimals = tokenpool.GetTokenDecimals GetIsSupportedChain = tokenpool.GetIsSupportedChain GetSupportedChains = tokenpool.GetSupportedChains - GetOnRamp = tokenpool.GetOnRamp - GetOffRamp = tokenpool.GetOffRamp GetRMNProxy = tokenpool.GetRMNProxy GetVerifyNotCursed = tokenpool.GetVerifyNotCursed GetDynamicConfig = tokenpool.GetDynamicConfig @@ -34,7 +32,6 @@ var ( GetCurrentRateLimiterState = tokenpool.GetCurrentRateLimiterState GetCursedSubjects = tokenpool.GetCursedSubjects GetAdminConfig = tokenpool.GetAdminConfig - GetMirroredPolicy = tokenpool.GetMirroredPolicy GetRemoteChainConfig = tokenpool.GetRemoteChainConfig GetFee = tokenpool.GetFee GetCCVAmount = tokenpool.GetCCVAmount diff --git a/pkg/ccip/bindings/tokenpool/lockrelease/reader.go b/pkg/ccip/bindings/tokenpool/lockrelease/reader.go index 5485cb797..dd8bd3161 100644 --- a/pkg/ccip/bindings/tokenpool/lockrelease/reader.go +++ b/pkg/ccip/bindings/tokenpool/lockrelease/reader.go @@ -57,8 +57,6 @@ var ( GetTokenDecimals = tokenpool.GetTokenDecimals GetIsSupportedChain = tokenpool.GetIsSupportedChain GetSupportedChains = tokenpool.GetSupportedChains - GetOnRamp = tokenpool.GetOnRamp - GetOffRamp = tokenpool.GetOffRamp GetRMNProxy = tokenpool.GetRMNProxy GetVerifyNotCursed = tokenpool.GetVerifyNotCursed GetDynamicConfig = tokenpool.GetDynamicConfig @@ -71,7 +69,6 @@ var ( GetCurrentRateLimiterState = tokenpool.GetCurrentRateLimiterState GetCursedSubjects = tokenpool.GetCursedSubjects GetAdminConfig = tokenpool.GetAdminConfig - GetMirroredPolicy = tokenpool.GetMirroredPolicy GetRemoteChainConfig = tokenpool.GetRemoteChainConfig GetFee = tokenpool.GetFee GetCCVAmount = tokenpool.GetCCVAmount diff --git a/pkg/ccip/bindings/tokenpool/lockreleaselockbox/reader.go b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/reader.go index 35503caeb..e7d718676 100644 --- a/pkg/ccip/bindings/tokenpool/lockreleaselockbox/reader.go +++ b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/reader.go @@ -47,8 +47,6 @@ var ( GetTokenDecimals = tokenpool.GetTokenDecimals GetIsSupportedChain = tokenpool.GetIsSupportedChain GetSupportedChains = tokenpool.GetSupportedChains - GetOnRamp = tokenpool.GetOnRamp - GetOffRamp = tokenpool.GetOffRamp GetRMNProxy = tokenpool.GetRMNProxy GetVerifyNotCursed = tokenpool.GetVerifyNotCursed GetDynamicConfig = tokenpool.GetDynamicConfig @@ -61,7 +59,6 @@ var ( GetCurrentRateLimiterState = tokenpool.GetCurrentRateLimiterState GetCursedSubjects = tokenpool.GetCursedSubjects GetAdminConfig = tokenpool.GetAdminConfig - GetMirroredPolicy = tokenpool.GetMirroredPolicy GetRemoteChainConfig = tokenpool.GetRemoteChainConfig GetFee = tokenpool.GetFee GetCCVAmount = tokenpool.GetCCVAmount diff --git a/pkg/ccip/bindings/tokenpool/reader.go b/pkg/ccip/bindings/tokenpool/reader.go index ce6b466f9..823978193 100644 --- a/pkg/ccip/bindings/tokenpool/reader.go +++ b/pkg/ccip/bindings/tokenpool/reader.go @@ -88,54 +88,19 @@ var GetSupportedChains = tvm.NewNoArgsGetter(tvm.NoArgsOpts[[]uint64]{ }), }) -// GetOnRamp gets the onRamp address for a given remote chain selector. +// GetRMNProxy gets the RMN proxy address, or nil when proxy updates are disabled. // -// On-chain: get fun onRamp(remoteChainSelector: uint64): address? -var GetOnRamp = tvm.Getter[uint64, *address.Address]{ - Name: "onRamp", - Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (*address.Address, error) { - isNil, err := r.IsNil(0) - if err != nil { - return nil, fmt.Errorf("error checking IsNil(0) - onRamp: %w", err) - } - if isNil { - return nil, nil - } - addrSlice, err := r.Slice(0) - if err != nil { - return nil, fmt.Errorf("error getting Slice(0) - onRamp: %w", err) - } - return addrSlice.LoadAddr() - }), -} - -// GetOffRamp gets the offRamp address for a given remote chain selector. -// -// On-chain: get fun offRamp(remoteChainSelector: uint64): address? -var GetOffRamp = tvm.Getter[uint64, *address.Address]{ - Name: "offRamp", +// On-chain: get fun getRMNProxy(): address? +var GetRMNProxy = tvm.NewNoArgsGetter(tvm.NoArgsOpts[*address.Address]{ + Name: "getRMNProxy", Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (*address.Address, error) { isNil, err := r.IsNil(0) if err != nil { - return nil, fmt.Errorf("error checking IsNil(0) - offRamp: %w", err) + return nil, fmt.Errorf("error checking IsNil(0) - getRMNProxy: %w", err) } if isNil { return nil, nil } - addrSlice, err := r.Slice(0) - if err != nil { - return nil, fmt.Errorf("error getting Slice(0) - offRamp: %w", err) - } - return addrSlice.LoadAddr() - }), -} - -// GetRMNProxy gets the RMN proxy address. -// -// On-chain: get fun getRMNProxy(): address -var GetRMNProxy = tvm.NewNoArgsGetter(tvm.NoArgsOpts[*address.Address]{ - Name: "getRMNProxy", - Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (*address.Address, error) { addrSlice, err := r.Slice(0) if err != nil { return nil, fmt.Errorf("error getting Slice(0) - getRMNProxy: %w", err) @@ -212,20 +177,19 @@ var GetDynamicConfig = tvm.NewNoArgsGetter(tvm.NoArgsOpts[DynamicConfig]{ cfg.FeeAdmin = addr } - // Decode the allowedDepositNamespaces dictionary (map). + // Decode the allowedDepositNamespaces dictionary (map - a unit + // value set; presence of a key means the namespace is allowed). dictCell, err := r.Cell(3) //nolint:mnd // index 3 for the 4th return value (allowedDepositNamespaces) if err != nil { return cfg, fmt.Errorf("error getting Cell(3) - allowedDepositNamespaces: %w", err) } - if dictCell == nil { - cfg.AllowedDepositNamespaces = tlbe.NewEmptyDict[uint32, bool]() - } else { - dict := dictCell.AsDict(32) // uint32 keys - depositNamespaces, err := tlbe.NewDictFromDictionary[uint32, bool](dict) - if err != nil { - return cfg, fmt.Errorf("error decoding allowedDepositNamespaces dict: %w", err) - } - cfg.AllowedDepositNamespaces = depositNamespaces + var dict *cell.Dictionary + if dictCell != nil { + dict = dictCell.AsDict(32) // uint32 keys + } + cfg.AllowedDepositNamespaces, err = tlbe.NewDictFromDictionary[uint32, struct{}](dict) + if err != nil { + return cfg, fmt.Errorf("error converting dict to tlbe.Dict - allowedDepositNamespaces: %w", err) } return cfg, nil @@ -440,26 +404,6 @@ var GetAdminConfig = tvm.NewNoArgsGetter(tvm.NoArgsOpts[AdminConfig]{ }), }) -// GetMirroredPolicy gets the mirrored policy (onRamps, offRamps, cursedSubjects). -// -// On-chain: fun TokenPool.getMirroredPolicy(self): TokenPool_MirroredPolicy -var GetMirroredPolicy = tvm.NewNoArgsGetter(tvm.NoArgsOpts[MirroredPolicy]{ - Name: "getMirroredPolicy", - Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (MirroredPolicy, error) { - c, err := r.Cell(0) - if err != nil { - return MirroredPolicy{}, fmt.Errorf("error getting Cell(0) - mirroredPolicy: %w", err) - } - - var mp MirroredPolicy - if err := tlb.LoadFromCell(&mp, c.BeginParse()); err != nil { - return MirroredPolicy{}, fmt.Errorf("error decoding MirroredPolicy: %w", err) - } - - return mp, nil - }), -}) - // GetRemoteChainConfig gets the remote chain config for a given chain selector. // // On-chain: fun TokenPool.getRemoteChainConfig(self, remoteChainSelector: uint64): TokenPool_RemoteChainConfig? diff --git a/pkg/ccip/bindings/tokenpool/types.go b/pkg/ccip/bindings/tokenpool/types.go index 3046950c0..23fa0fd3c 100644 --- a/pkg/ccip/bindings/tokenpool/types.go +++ b/pkg/ccip/bindings/tokenpool/types.go @@ -47,35 +47,29 @@ const ( // --- Data types (no opcodes) --- // DynamicConfig holds the router and admin addresses for the pool. -type DynamicConfig struct { - Router *address.Address `tlb:"addr"` - RateLimitAdmin *address.Address `tlb:"addr"` - FeeAdmin *address.Address `tlb:"addr"` - AllowedDepositNamespaces *tlbe.Dict[uint32, bool] `tlb:"."` -} - -// MirroredPolicy holds on/off ramp addresses and cursed subjects. // -// Dict fields use *cell.Dictionary rather than *tlbe.Dict: tonutils-go's tlb -// encoder (used to build init data for contract deploys, see -// deployment/utils/operation/deploy_ton_contract.go) type-asserts "dict N" -// fields directly to *cell.Dictionary and panics on any other type. -type MirroredPolicy struct { - OnRamps *cell.Dictionary `tlb:"dict 64"` - OffRamps *cell.Dictionary `tlb:"dict 64"` - CursedSubjects CursedSubjects `tlb:"."` +// AllowedDepositNamespaces is a unit-value set (map on-chain): a key +// is present if the corresponding deployable namespace is allowed as a deposit source. +type DynamicConfig struct { + Router *address.Address `tlb:"addr"` + RateLimitAdmin *address.Address `tlb:"addr"` + FeeAdmin *address.Address `tlb:"addr"` + AllowedDepositNamespaces *tlbe.Dict[uint32, struct{}] `tlb:"."` +} + +// LocalPolicy holds the pool's independently managed RMN policy. It is +// intentionally not a mirror of the Router's cursed subjects: the pool owner may +// configure a distinct RMN proxy, or disable proxy updates by setting it to +// addr_none. Ramp access is no longer pool state: the Router is the only entry +// point of every pool (TokenPool.onlyRouter). +type LocalPolicy struct { + CursedSubjects CursedSubjects `tlb:"."` } -// CursedSubjects represents the set of cursed subjects (uint128 keys with empty values). +// CursedSubjects represents the set of cursed subjects (uint128 keys with empty +// values), i.e. map on-chain. type CursedSubjects struct { - Data *cell.Dictionary `tlb:"dict 128"` -} - -// RampUpdate represents a single ramp access update for a remote chain. -type RampUpdate struct { - RemoteChainSelector uint64 `tlb:"## 64"` - OnRamp *address.Address `tlb:"addr"` - OffRamp *address.Address `tlb:"addr"` + Data *tlbe.Dict[tlbe.Uint128, struct{}] `tlb:"."` } // RateLimitConfig represents a rate limiter configuration. @@ -120,10 +114,10 @@ type ChainUpdate struct { // RemoteChainConfig holds the configuration for a remote chain. type RemoteChainConfig struct { - RemoteTokenAddress *tlbe.Cell[common.CrossChainAddress] `tlb:"^"` - RemotePools *tlbe.Dict[*tlbe.Uint256, *cell.Cell] `tlb:"."` - RateLimiters RateLimiterPair `tlb:"^"` - FastFinalityRateLimiters RateLimiterPair `tlb:"^"` + RemoteTokenAddress *tlbe.Cell[common.CrossChainAddress] `tlb:"^"` + RemotePools *tlbe.Dict[tlbe.Uint256, *cell.Cell] `tlb:"."` + RateLimiters RateLimiterPair `tlb:"^"` + FastFinalityRateLimiters RateLimiterPair `tlb:"^"` } // RateLimitConfigArgs holds arguments for setting rate limit configs. @@ -260,11 +254,11 @@ type AdminConfig struct { } // Storage represents the TokenPool_Data storage layout shared by every TokenPool -// implementation (Mock, BurnMint, LockRelease, ...), each of which wraps it as +// implementation (BurnMint, LockRelease, ...), each of which wraps it as // `poolData: Cell` alongside its own pool-specific fields. type Storage struct { AdminConfig AdminConfig `tlb:"^"` - MirroredPolicy MirroredPolicy `tlb:"^"` + LocalPolicy LocalPolicy `tlb:"^"` TokenDecimals uint8 `tlb:"## 8"` RemoteChainConfigs *cell.Dictionary `tlb:"dict 64"` TokenTransferFeeConfigs *cell.Dictionary `tlb:"dict 64"` @@ -321,16 +315,16 @@ type SetAdvancedPoolHooks struct { // SetDeployableCode sets the Compiled Deployable code used to derive source-chain deposit accounts. type SetDeployableCode struct { - _ tlb.Magic `tlb:"#6c2a91e4" json:"-"` //nolint:revive // (opcode) should stay uninitialized + _ tlb.Magic `tlb:"#3868e309" json:"-"` //nolint:revive // (opcode) should stay uninitialized QueryID uint64 `tlb:"## 64"` DeployableCode *cell.Cell `tlb:"maybe ^"` } // SetAllowedDepositNamespaces sets the Deployables namespaces the pool accepts as deposit sources. type SetAllowedDepositNamespaces struct { - _ tlb.Magic `tlb:"#1f8e33c2" json:"-"` //nolint:revive // (opcode) should stay uninitialized - QueryID uint64 `tlb:"## 64"` - AllowedDepositNamespaces *tlbe.Dict[uint32, bool] `tlb:"."` + _ tlb.Magic `tlb:"#84384142" json:"-"` //nolint:revive // (opcode) should stay uninitialized + QueryID uint64 `tlb:"## 64"` + AllowedDepositNamespaces *tlbe.Dict[uint32, struct{}] `tlb:"."` } // SetRateLimitConfig sets the rate limit configurations. @@ -348,13 +342,6 @@ type ApplyTokenTransferFeeConfigUpdates struct { DisableChainSelectors common.SnakedCell[ChainSelector] `tlb:"^"` } -// UpdateRampAccess updates ramp access for chains. -type UpdateRampAccess struct { - _ tlb.Magic `tlb:"#e30764be" json:"-"` //nolint:revive // (opcode) should stay uninitialized - QueryID uint64 `tlb:"## 64"` - Updates common.SnakedCell[RampUpdate] `tlb:"^"` -} - // SetRMNProxy sets the RMN proxy address. type SetRMNProxy struct { _ tlb.Magic `tlb:"#9929b642" json:"-"` //nolint:revive // (opcode) should stay uninitialized @@ -624,12 +611,6 @@ type ChainUpdatesApplied struct { QueryID uint64 `tlb:"## 64"` } -// RampAccessUpdatesApplied is replied on UpdateRampAccess to confirm the tx and return excess. -type RampAccessUpdatesApplied struct { - _ tlb.Magic `tlb:"#d7f5c563" json:"-"` //nolint:revive // (opcode) should stay uninitialized - QueryID uint64 `tlb:"## 64"` -} - // FeeConfigApplied is replied on ApplyTokenTransferFeeConfigUpdates to confirm the tx // and return excess. type FeeConfigApplied struct { @@ -660,14 +641,14 @@ type AdvancedPoolHooksSet struct { // DeployableCodeSet confirms the deployable code was set. type DeployableCodeSet struct { - _ tlb.Magic `tlb:"#09d4a7b1" json:"-"` //nolint:revive // (opcode) should stay uninitialized + _ tlb.Magic `tlb:"#89d602e5" json:"-"` //nolint:revive // (opcode) should stay uninitialized QueryID uint64 `tlb:"## 64"` DeployableCode *cell.Cell `tlb:"maybe ^"` } // AllowedDepositNamespacesSet confirms the allowed deposit namespaces were set. type AllowedDepositNamespacesSet struct { - _ tlb.Magic `tlb:"#7a53c9f4" json:"-"` //nolint:revive // (opcode) should stay uninitialized + _ tlb.Magic `tlb:"#c1ffe3a6" json:"-"` //nolint:revive // (opcode) should stay uninitialized QueryID uint64 `tlb:"## 64"` } @@ -728,13 +709,6 @@ type RateLimitConfigured struct { Args RateLimitConfigArgs `tlb:"."` } -// RampAccessUpdated is emitted when ramp access is updated. -type RampAccessUpdated struct { - RemoteChainSelector uint64 `tlb:"## 64"` - OnRamp *address.Address `tlb:"addr"` - OffRamp *address.Address `tlb:"addr"` -} - // OutboundRateLimitConsumed is emitted when outbound rate-limit capacity is consumed. type OutboundRateLimitConsumed struct { RemoteChainSelector uint64 `tlb:"## 64"` @@ -820,7 +794,6 @@ var TLBs = tvm.MustNewTLBMap([]any{ SetAllowedDepositNamespaces{}, SetRateLimitConfig{}, ApplyTokenTransferFeeConfigUpdates{}, - UpdateRampAccess{}, SetRMNProxy{}, SetCursedSubjects{}, JettonWithdrawableWithdraw{}, @@ -854,7 +827,6 @@ var TLBs = tvm.MustNewTLBMap([]any{ DeployableCodeSet{}, AllowedDepositNamespacesSet{}, ChainUpdatesApplied{}, - RampAccessUpdatesApplied{}, FeeConfigApplied{}, // AdvancedPoolHooks outgoing (sent from TokenPool to hooks contract) // GetCCVs (registered above) is reused for the pool→hooks hop. @@ -871,7 +843,6 @@ const ( TopicRemotePoolAdded = "TokenPool_RemotePoolAdded" TopicRemotePoolRemoved = "TokenPool_RemotePoolRemoved" TopicDynamicConfigSet = "TokenPool_DynamicConfigSet" - TopicRampAccessUpdated = "TokenPool_RampAccessUpdated" TopicFinalityConfigSet = "TokenPool_FinalityConfigSet" TopicRateLimitConfigured = "TokenPool_RateLimitConfigured" TopicOutboundRateLimitConsumed = "TokenPool_OutboundRateLimitConsumed" diff --git a/pkg/ccip/bindings/tokenpool/types_test.go b/pkg/ccip/bindings/tokenpool/types_test.go new file mode 100644 index 000000000..7c2f7fe10 --- /dev/null +++ b/pkg/ccip/bindings/tokenpool/types_test.go @@ -0,0 +1,156 @@ +package tokenpool + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/require" + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/tvm/cell" + + "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tlbe" +) + +// TestDynamicConfig_AllowedDepositNamespaces_WireFormat verifies that modelling +// AllowedDepositNamespaces as *tlbe.Dict[uint32, struct{}] (tlb:".") produces +// exactly the same wire form as a raw *cell.Dictionary serialized through the +// "dict 32" tag: a map whose unit values are 0-bit inline leaves. +func TestDynamicConfig_AllowedDepositNamespaces_WireFormat(t *testing.T) { + namespaces := tlbe.NewDict[uint32, struct{}](map[uint32]struct{}{ + 1: {}, + 42: {}, + }) + + cfg := DynamicConfig{ + AllowedDepositNamespaces: namespaces, + } + + cfgCell, err := tlb.ToCell(cfg) + require.NoError(t, err) + + // Same layout, but the dict is a raw *cell.Dictionary through the "dict 32" + // tag path (the encoding used before this change). + rawDict := cell.NewDict(32) + for k := range namespaces.AsMap() { + keyCell := cell.BeginCell().MustStoreUInt(uint64(k), 32).EndCell() + unitCell := cell.BeginCell().EndCell() // map<_,()> value: 0 bits, 0 refs + require.NoError(t, rawDict.Set(keyCell, unitCell)) + } + + rawStruct := struct { + Router *address.Address `tlb:"addr"` + RateLimitAdmin *address.Address `tlb:"addr"` + FeeAdmin *address.Address `tlb:"addr"` + AllowedDepositNamespaces *cell.Dictionary `tlb:"dict 32"` + }{AllowedDepositNamespaces: rawDict} + rawCell, err := tlb.ToCell(rawStruct) + require.NoError(t, err) + + require.Equal(t, rawCell.Hash(), cfgCell.Hash(), + "tlbe.Dict struct{} encoding must match the raw dict 32 wire form") +} + +// TestCursedSubjects_WireFormat verifies that modelling CursedSubjects.Data as +// *tlbe.Dict[tlbe.Uint128, struct{}] (tlb:".") produces the same wire form as a +// raw *cell.Dictionary serialized through the "dict 128" tag: a map +// with 0-bit unit values, inlined into the enclosing cell. +func TestCursedSubjects_WireFormat(t *testing.T) { + subjects := tlbe.NewDict[tlbe.Uint128, struct{}](map[tlbe.Uint128]struct{}{ + *tlbe.NewUint128(big.NewInt(1)): {}, + *tlbe.NewUint128(big.NewInt(42)): {}, + }) + + got, err := tlb.ToCell(CursedSubjects{Data: subjects}) + require.NoError(t, err) + + rawDict := cell.NewDict(128) + for k := range subjects.AsMap() { + keyCell := cell.BeginCell().MustStoreBigInt(k.ToBigInt(), 128).EndCell() + unitCell := cell.BeginCell().EndCell() // map<_,()> value: 0 bits, 0 refs + require.NoError(t, rawDict.Set(keyCell, unitCell)) + } + + want, err := tlb.ToCell(struct { + Data *cell.Dictionary `tlb:"dict 128"` + }{Data: rawDict}) + require.NoError(t, err) + + require.Equal(t, want.Hash(), got.Hash(), + "tlbe.Dict[Uint128, struct{}] encoding must match the raw dict 128 wire form") +} + +// TestCursedSubjects_RoundTrip verifies encode/decode via the tlb:"." marshaller. +func TestCursedSubjects_RoundTrip(t *testing.T) { + subjects := tlbe.NewDict[tlbe.Uint128, struct{}](map[tlbe.Uint128]struct{}{ + *tlbe.NewUint128(big.NewInt(7)): {}, + }) + + c, err := tlb.ToCell(CursedSubjects{Data: subjects}) + require.NoError(t, err) + + var decoded CursedSubjects + require.NoError(t, tlb.LoadFromCell(&decoded, c.BeginParse())) + + _, ok := decoded.Data.Get(*tlbe.NewUint128(big.NewInt(7))) + require.True(t, ok) + require.Equal(t, 1, decoded.Data.Len()) +} + +// TestRemoteChainConfig_RemotePools_Decode is a regression test: RemotePools keys +// used to be *tlbe.Uint256, which panics on decode (pointer keys are not +// tlb.Unmarshaler). Value-typed Uint256 keys must decode, mirroring the +// RemotePools field shape with a plain payload to isolate key behaviour. +func TestRemoteChainConfig_RemotePools_Decode(t *testing.T) { + type remotePoolsHolder struct { + RemotePools *tlbe.Dict[tlbe.Uint256, uint64] `tlb:"."` + } + + cfg := remotePoolsHolder{ + RemotePools: tlbe.NewDict[tlbe.Uint256, uint64](map[tlbe.Uint256]uint64{ + *tlbe.NewUint256(big.NewInt(5)): 123, + }), + } + + c, err := tlb.ToCell(cfg) + require.NoError(t, err) + + var decoded remotePoolsHolder + require.NoError(t, tlb.LoadFromCell(&decoded, c.BeginParse())) + require.Len(t, decoded.RemotePools.AsMap(), 1) + + got, ok := decoded.RemotePools.Get(*tlbe.NewUint256(big.NewInt(5))) + require.True(t, ok) + require.Equal(t, uint64(123), got) +} + +// TestDynamicConfig_AllowedDepositNamespaces_RoundTrip verifies encode/decode +// through tonutils-go's tlb loader (using the tlb:"." marshaller path). +func TestDynamicConfig_AllowedDepositNamespaces_RoundTrip(t *testing.T) { + namespaces := tlbe.NewDict[uint32, struct{}](map[uint32]struct{}{ + 7: {}, + 13: {}, + }) + + cfg := DynamicConfig{AllowedDepositNamespaces: namespaces} + + c, err := tlb.ToCell(cfg) + require.NoError(t, err) + + var decoded DynamicConfig + require.NoError(t, tlb.LoadFromCell(&decoded, c.BeginParse())) + require.Len(t, decoded.AllowedDepositNamespaces.AsMap(), 2) + _, ok := decoded.AllowedDepositNamespaces.Get(7) + require.True(t, ok) + _, ok = decoded.AllowedDepositNamespaces.Get(13) + require.True(t, ok) + + // Empty set round-trips as an empty map. + empty := DynamicConfig{AllowedDepositNamespaces: tlbe.NewEmptyDict[uint32, struct{}]()} + emptyCell, err := tlb.ToCell(empty) + require.NoError(t, err) + + var decodedEmpty DynamicConfig + require.NoError(t, tlb.LoadFromCell(&decodedEmpty, emptyCell.BeginParse())) + require.Empty(t, decodedEmpty.AllowedDepositNamespaces.AsMap()) +} diff --git a/pkg/ton/tlbe/tlbe.go b/pkg/ton/tlbe/tlbe.go index 04a1be52d..2f53d7e90 100644 --- a/pkg/ton/tlbe/tlbe.go +++ b/pkg/ton/tlbe/tlbe.go @@ -17,6 +17,7 @@ import ( type Cell[T any] = src.Cell[T] type Dict[K comparable, V any] = src.Dict[K, V] +type Uint128 = src.Uint128 type Uint160 = src.Uint160 type Uint256 = src.Uint256 @@ -49,5 +50,6 @@ func NewEmptyDict[K comparable, V any]() *Dict[K, V] { return src.NewEmptyDict[K, V]() } +var NewUint128 = src.NewUint128 var NewUint160 = src.NewUint160 var NewUint256 = src.NewUint256 diff --git a/scripts/oplint/lock.nix b/scripts/oplint/lock.nix index 0047cead7..d6250aec4 100644 --- a/scripts/oplint/lock.nix +++ b/scripts/oplint/lock.nix @@ -1,3 +1,3 @@ {pkgs}: { - oplint = "sha256-lHYNeIUSxyJyHgoJcd/VjwLOynH2AP5Fp8nK4YpKhhU="; + oplint = "sha256-CvAHD2AeJq51erMWhXj+u2FHFBIyREuGX7LjGbQagXk="; }