-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement encode/decode settlement post interaction data
- Loading branch information
Showing
3 changed files
with
348 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 132 additions & 0 deletions
132
pkg/oneinch/fusionorder/settlement_post_interaction_data_encode_decode.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package fusionorder | ||
|
||
import ( | ||
"bytes" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/KyberNetwork/tradinglib/pkg/oneinch/utils" | ||
"github.com/ethereum/go-ethereum/common" | ||
) | ||
|
||
const ( | ||
resolverFeeFlag = 0x01 | ||
integratorFeeFlag = 0x02 | ||
customReceiverFlag = 0x04 | ||
whitelistShift = 3 | ||
) | ||
|
||
func resolverFeeEnabled(flags byte) bool { | ||
return flags&resolverFeeFlag == resolverFeeFlag | ||
} | ||
|
||
func integratorFeeEnabled(flags byte) bool { | ||
return flags&integratorFeeFlag == integratorFeeFlag | ||
} | ||
|
||
func hasCustomReceiver(flags byte) bool { | ||
return flags&customReceiverFlag == customReceiverFlag | ||
} | ||
|
||
func resolversCount(flags byte) byte { | ||
return flags >> whitelistShift | ||
} | ||
|
||
func DecodeSettlementPostInteractionData(extraData string) (SettlementPostInteractionData, error) { | ||
if !utils.IsHexString(extraData) { | ||
return SettlementPostInteractionData{}, errors.New("invalid auction details data") | ||
} | ||
|
||
data, err := hex.DecodeString(utils.Trim0x(extraData)) | ||
if err != nil { | ||
return SettlementPostInteractionData{}, fmt.Errorf("decode settlement post interaction data: %w", err) | ||
} | ||
|
||
flags := data[len(data)-1] | ||
|
||
bankFee := big.NewInt(0) | ||
var integratorFee IntegratorFee | ||
var customReceiver common.Address | ||
resolvingStartTime := big.NewInt(0) | ||
|
||
if resolverFeeEnabled(flags) { | ||
bankFee.SetBytes(data[:4]) | ||
data = data[4:] | ||
} | ||
|
||
if integratorFeeEnabled(flags) { | ||
integratorFeeRatio := new(big.Int).SetBytes(data[:2]) | ||
integratorAddress := common.BytesToAddress(data[2:22]) | ||
integratorFee = IntegratorFee{ | ||
Ratio: integratorFeeRatio, | ||
Receiver: integratorAddress, | ||
} | ||
|
||
data = data[22:] | ||
|
||
if hasCustomReceiver(flags) { | ||
customReceiver = common.BytesToAddress(data[:20]) | ||
data = data[20:] | ||
} | ||
} | ||
|
||
resolvingStartTime = new(big.Int).SetBytes(data[:4]) | ||
data = data[4:] | ||
|
||
whitelistCount := resolversCount(flags) | ||
whitelist := make([]WhitelistItem, 0, whitelistCount) | ||
|
||
for i := byte(0); i < whitelistCount; i++ { | ||
var address AddressHalf | ||
copy(address[:], data[:addressHalfLength]) | ||
data = data[addressHalfLength:] | ||
|
||
delay := new(big.Int).SetBytes(data[:2]) | ||
data = data[2:] | ||
|
||
whitelist = append(whitelist, WhitelistItem{ | ||
AddressHalf: address, | ||
Delay: uint16(delay.Uint64()), | ||
}) | ||
} | ||
|
||
return SettlementPostInteractionData{ | ||
Whitelist: whitelist, | ||
IntegratorFee: integratorFee, | ||
BankFee: bankFee, | ||
ResolvingStartTime: resolvingStartTime, | ||
CustomReceiver: customReceiver, | ||
}, nil | ||
} | ||
|
||
func (s SettlementPostInteractionData) Encode() string { | ||
buf := new(bytes.Buffer) | ||
var flags byte | ||
if s.BankFee != nil && s.BankFee.Cmp(big.NewInt(0)) != 0 { | ||
flags |= resolverFeeFlag | ||
buf.Write(utils.PadOrTrim(s.BankFee.Bytes(), 4)) | ||
} | ||
if s.IntegratorFee.Ratio != nil && s.IntegratorFee.Ratio.Cmp(big.NewInt(0)) != 0 { | ||
flags |= integratorFeeFlag | ||
buf.Write(utils.PadOrTrim(s.IntegratorFee.Ratio.Bytes(), 2)) | ||
buf.Write(s.IntegratorFee.Receiver.Bytes()) | ||
if s.CustomReceiver != (common.Address{}) { | ||
flags |= customReceiverFlag | ||
buf.Write(s.CustomReceiver.Bytes()) | ||
} | ||
} | ||
buf.Write(utils.PadOrTrim(s.ResolvingStartTime.Bytes(), 4)) | ||
|
||
for _, wl := range s.Whitelist { | ||
buf.Write(wl.AddressHalf[:]) | ||
buf.Write(utils.PadOrTrim(big.NewInt(int64(int(wl.Delay))).Bytes(), 2)) | ||
} | ||
|
||
flags |= byte(len(s.Whitelist)) << whitelistShift | ||
|
||
buf.WriteByte(flags) | ||
|
||
return utils.Add0x(hex.EncodeToString(buf.Bytes())) | ||
} |
195 changes: 195 additions & 0 deletions
195
pkg/oneinch/fusionorder/settlement_post_interaction_data_encode_decode_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
package fusionorder_test | ||
|
||
import ( | ||
"math/big" | ||
"testing" | ||
|
||
"github.com/KyberNetwork/tradinglib/pkg/oneinch/fusionorder" | ||
"github.com/KyberNetwork/tradinglib/pkg/oneinch/fusionutils" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// Those tests are copied from | ||
// https://github.com/1inch/fusion-sdk/blob/8721c62612b08cc7c0e01423a1bdd62594e7b8d0/src/fusion-order/settlement-post-interaction-data/settlement-post-interaction-data.spec.ts#L6 | ||
func TestSettlementPostInteractionData(t *testing.T) { | ||
t.Run("Should encode/decode with bank fee and whitelist", func(t *testing.T) { | ||
data, err := fusionorder.NewSettlementPostInteractionDataFromSettlementSuffixData( | ||
fusionorder.SettlementSuffixData{ | ||
BankFee: big.NewInt(1), | ||
ResolvingStartTime: big.NewInt(1708117482), | ||
Whitelist: []fusionorder.AuctionWhitelistItem{ | ||
{ | ||
Address: common.BigToAddress(big.NewInt(0)), | ||
AllowFrom: big.NewInt(0), | ||
}, | ||
}, | ||
}, | ||
) | ||
require.NoError(t, err) | ||
|
||
encoded := data.Encode() | ||
|
||
assert.Equal(t, 44, len(encoded)) | ||
|
||
decoded, err := fusionorder.DecodeSettlementPostInteractionData(encoded) | ||
require.NoError(t, err) | ||
|
||
assertSettlementPostInteractionDataEqual(t, data, decoded) | ||
}) | ||
|
||
t.Run("Should encode/decode with no fees and whitelist", func(t *testing.T) { | ||
data, err := fusionorder.NewSettlementPostInteractionDataFromSettlementSuffixData( | ||
fusionorder.SettlementSuffixData{ | ||
ResolvingStartTime: big.NewInt(1708117482), | ||
Whitelist: []fusionorder.AuctionWhitelistItem{ | ||
{ | ||
Address: common.BigToAddress(big.NewInt(0)), | ||
AllowFrom: big.NewInt(0), | ||
}, | ||
}, | ||
}, | ||
) | ||
require.NoError(t, err) | ||
|
||
encoded := data.Encode() | ||
|
||
assert.Equal(t, 36, len(encoded)) | ||
|
||
decoded, err := fusionorder.DecodeSettlementPostInteractionData(encoded) | ||
require.NoError(t, err) | ||
|
||
assertSettlementPostInteractionDataEqual(t, data, decoded) | ||
}) | ||
|
||
t.Run("Should encode/decode with fees and whitelist", func(t *testing.T) { | ||
data, err := fusionorder.NewSettlementPostInteractionDataFromSettlementSuffixData( | ||
fusionorder.SettlementSuffixData{ | ||
BankFee: big.NewInt(0), | ||
ResolvingStartTime: big.NewInt(1708117482), | ||
Whitelist: []fusionorder.AuctionWhitelistItem{ | ||
{ | ||
Address: common.BigToAddress(big.NewInt(0)), | ||
AllowFrom: big.NewInt(0), | ||
}, | ||
}, | ||
IntegratorFee: fusionorder.IntegratorFee{ | ||
Ratio: fusionutils.BpsToRatioFormat(10), | ||
Receiver: common.BigToAddress(big.NewInt(1)), | ||
}, | ||
}, | ||
) | ||
require.NoError(t, err) | ||
|
||
decoded, err := fusionorder.DecodeSettlementPostInteractionData(data.Encode()) | ||
require.NoError(t, err) | ||
|
||
assertSettlementPostInteractionDataEqual(t, data, decoded) | ||
}) | ||
t.Run("Should encode/decode with fees, custom receiver and whitelist", func(t *testing.T) { | ||
data, err := fusionorder.NewSettlementPostInteractionDataFromSettlementSuffixData( | ||
fusionorder.SettlementSuffixData{ | ||
BankFee: big.NewInt(0), | ||
ResolvingStartTime: big.NewInt(1708117482), | ||
Whitelist: []fusionorder.AuctionWhitelistItem{ | ||
{ | ||
Address: common.BigToAddress(big.NewInt(0)), | ||
AllowFrom: big.NewInt(0), | ||
}, | ||
}, | ||
IntegratorFee: fusionorder.IntegratorFee{ | ||
Ratio: fusionutils.BpsToRatioFormat(10), | ||
Receiver: common.BigToAddress(big.NewInt(1)), | ||
}, | ||
CustomReceiver: common.BigToAddress(big.NewInt(1337)), | ||
}, | ||
) | ||
require.NoError(t, err) | ||
|
||
decoded, err := fusionorder.DecodeSettlementPostInteractionData(data.Encode()) | ||
require.NoError(t, err) | ||
|
||
assertSettlementPostInteractionDataEqual(t, data, decoded) | ||
}) | ||
|
||
t.Run("Should generate correct whitelist", func(t *testing.T) { | ||
start := big.NewInt(1708117482) | ||
|
||
data, err := fusionorder.NewSettlementPostInteractionDataFromSettlementSuffixData( | ||
fusionorder.SettlementSuffixData{ | ||
ResolvingStartTime: start, | ||
Whitelist: []fusionorder.AuctionWhitelistItem{ | ||
{ | ||
Address: common.BigToAddress(big.NewInt(2)), | ||
AllowFrom: new(big.Int).Add(start, big.NewInt(1_000)), | ||
}, | ||
{ | ||
Address: common.BigToAddress(big.NewInt(0)), | ||
AllowFrom: new(big.Int).Sub(start, big.NewInt(10)), // should be set to start | ||
}, | ||
{ | ||
Address: common.BigToAddress(big.NewInt(1)), | ||
AllowFrom: new(big.Int).Add(start, big.NewInt(10)), | ||
}, | ||
{ | ||
Address: common.BigToAddress(big.NewInt(3)), | ||
AllowFrom: new(big.Int).Add(start, big.NewInt(10)), | ||
}, | ||
}, | ||
}, | ||
) | ||
require.NoError(t, err) | ||
|
||
expectedWhitelist := []fusionorder.WhitelistItem{ | ||
{ | ||
AddressHalf: fusionorder.AddressHalf{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
Delay: 0, | ||
}, | ||
{ | ||
AddressHalf: fusionorder.AddressHalf{0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, | ||
Delay: 10, | ||
}, | ||
{ | ||
AddressHalf: fusionorder.AddressHalf{0, 0, 0, 0, 0, 0, 0, 0, 0, 3}, | ||
Delay: 0, | ||
}, | ||
{ | ||
AddressHalf: fusionorder.AddressHalf{0, 0, 0, 0, 0, 0, 0, 0, 0, 2}, | ||
Delay: 990, | ||
}, | ||
} | ||
|
||
assert.ElementsMatch(t, expectedWhitelist, data.Whitelist) | ||
|
||
assert.True(t, | ||
data.CanExecuteAt(common.BigToAddress(big.NewInt(1)), | ||
new(big.Int).Add(start, big.NewInt(10))), | ||
) | ||
assert.False(t, | ||
data.CanExecuteAt(common.BigToAddress(big.NewInt(1)), | ||
new(big.Int).Add(start, big.NewInt(9))), | ||
) | ||
assert.True(t, | ||
data.CanExecuteAt(common.BigToAddress(big.NewInt(3)), | ||
new(big.Int).Add(start, big.NewInt(10))), | ||
) | ||
assert.False(t, | ||
data.CanExecuteAt(common.BigToAddress(big.NewInt(3)), | ||
new(big.Int).Add(start, big.NewInt(9))), | ||
) | ||
assert.False(t, | ||
data.CanExecuteAt(common.BigToAddress(big.NewInt(2)), | ||
new(big.Int).Add(start, big.NewInt(50))), | ||
) | ||
}) | ||
} | ||
|
||
func assertSettlementPostInteractionDataEqual(t *testing.T, expected, actual fusionorder.SettlementPostInteractionData) { | ||
assert.ElementsMatch(t, expected.Whitelist, actual.Whitelist) | ||
assert.Equal(t, expected.IntegratorFee.Ratio, actual.IntegratorFee.Ratio) | ||
assert.Equal(t, expected.IntegratorFee.Receiver, actual.IntegratorFee.Receiver) | ||
assert.Equal(t, expected.BankFee, actual.BankFee) | ||
assert.Equal(t, expected.ResolvingStartTime, actual.ResolvingStartTime) | ||
assert.Equal(t, expected.CustomReceiver, actual.CustomReceiver) | ||
} |