mirror of
https://github.com/luxfi/dex.git
synced 2026-09-24 00:26:31 +00:00
fix: gofmt -s across repo (CI format check)
This commit is contained in:
@@ -12,11 +12,11 @@
|
||||
// mode this daemon supports.
|
||||
//
|
||||
// Smoke flow:
|
||||
// 1. build dex-api-server
|
||||
// 2. spawn it with a free port
|
||||
// 3. POST /api/order to place a limit-buy order
|
||||
// 4. GET /api/orderbook and confirm the order appears on the bid side
|
||||
// 5. SIGTERM and confirm graceful exit
|
||||
// 1. build dex-api-server
|
||||
// 2. spawn it with a free port
|
||||
// 3. POST /api/order to place a limit-buy order
|
||||
// 4. GET /api/orderbook and confirm the order appears on the bid side
|
||||
// 5. SIGTERM and confirm graceful exit
|
||||
//
|
||||
// Multi-node consensus is provided by an entirely different binary
|
||||
// (cmd/dag-network, build-tagged zmqtest) that wires pkg/consensus
|
||||
|
||||
+15
-13
@@ -30,8 +30,8 @@ var (
|
||||
|
||||
// Gas estimates for approval operations
|
||||
const (
|
||||
gasERC20Approve uint64 = 50_000
|
||||
gasPermit2Approve uint64 = 60_000
|
||||
gasERC20Approve uint64 = 50_000
|
||||
gasPermit2Approve uint64 = 60_000
|
||||
)
|
||||
|
||||
// MaxUint256 is the maximum approval amount (2^256 - 1)
|
||||
@@ -68,9 +68,9 @@ type ApprovalRequest struct {
|
||||
|
||||
// ApprovalResponse is returned from the approval check endpoint.
|
||||
type ApprovalResponse struct {
|
||||
Approved bool `json:"approved"`
|
||||
Allowance string `json:"allowance"`
|
||||
NeedsApproval bool `json:"needsApproval"`
|
||||
Approved bool `json:"approved"`
|
||||
Allowance string `json:"allowance"`
|
||||
NeedsApproval bool `json:"needsApproval"`
|
||||
ApproveTx *UnsignedTxResponse `json:"approveTx,omitempty"`
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ type Permit2Request struct {
|
||||
|
||||
// Permit2Response is returned from the Permit2 check endpoint.
|
||||
type Permit2Response struct {
|
||||
Permit2Approved bool `json:"permit2Approved"`
|
||||
Permit2Allowance string `json:"permit2Allowance"`
|
||||
Permit2Approved bool `json:"permit2Approved"`
|
||||
Permit2Allowance string `json:"permit2Allowance"`
|
||||
SignatureRequest *EIP712SignRequest `json:"signatureRequest,omitempty"`
|
||||
}
|
||||
|
||||
@@ -175,8 +175,9 @@ func (r *Permit2Request) Validate() error {
|
||||
// BuildERC20ApproveCalldata encodes ERC20 approve(spender, amount).
|
||||
//
|
||||
// ABI layout (after 4-byte selector):
|
||||
// [0:32] spender (address, left-padded to 32 bytes)
|
||||
// [32:64] amount (uint256)
|
||||
//
|
||||
// [0:32] spender (address, left-padded to 32 bytes)
|
||||
// [32:64] amount (uint256)
|
||||
func BuildERC20ApproveCalldata(spender string, amount *big.Int) []byte {
|
||||
data := make([]byte, 68) // 4 selector + 2*32 params
|
||||
copy(data[0:4], selectorERC20Approve[:])
|
||||
@@ -209,10 +210,11 @@ func BuildERC20AllowanceCalldata(owner, spender string) []byte {
|
||||
// BuildPermit2ApproveCalldata encodes Permit2 approve(token, spender, amount, expiration).
|
||||
//
|
||||
// ABI layout (after 4-byte selector):
|
||||
// [0:32] token (address)
|
||||
// [32:64] spender (address)
|
||||
// [64:96] amount (uint160)
|
||||
// [96:128] expiration (uint48)
|
||||
//
|
||||
// [0:32] token (address)
|
||||
// [32:64] spender (address)
|
||||
// [64:96] amount (uint160)
|
||||
// [96:128] expiration (uint48)
|
||||
func BuildPermit2ApproveCalldata(token, spender string, amount *big.Int, expiration int64) []byte {
|
||||
data := make([]byte, 132) // 4 + 4*32
|
||||
copy(data[0:4], selectorPermit2Approve[:])
|
||||
|
||||
@@ -114,12 +114,12 @@ func BuildExactInputCalldata(path []string, amountIn, amountOutMin, sqrtPriceLim
|
||||
|
||||
data := make([]byte, 0, 4+32*5+len(pathPadded))
|
||||
data = append(data, SelectorExactInput...)
|
||||
data = append(data, lxrPadUint256(big.NewInt(128))...) // offset to path data (4 * 32 = 128)
|
||||
data = append(data, lxrPadUint256(amountIn)...) // amountIn
|
||||
data = append(data, lxrPadUint256(amountOutMin)...) // amountOutMinimum
|
||||
data = append(data, lxrPadUint256(sqrtPriceLimitX96)...) // sqrtPriceLimitX96
|
||||
data = append(data, lxrPadUint256(big.NewInt(128))...) // offset to path data (4 * 32 = 128)
|
||||
data = append(data, lxrPadUint256(amountIn)...) // amountIn
|
||||
data = append(data, lxrPadUint256(amountOutMin)...) // amountOutMinimum
|
||||
data = append(data, lxrPadUint256(sqrtPriceLimitX96)...) // sqrtPriceLimitX96
|
||||
data = append(data, lxrPadUint256(big.NewInt(int64(len(packedPath))))...) // path length
|
||||
data = append(data, pathPadded...) // path data
|
||||
data = append(data, pathPadded...) // path data
|
||||
|
||||
return "0x" + hex.EncodeToString(data), nil
|
||||
}
|
||||
|
||||
@@ -481,13 +481,13 @@ func (p *Provider) ExecuteSwap(ctx context.Context, req gateway.SwapRequest) (st
|
||||
|
||||
// v3PoolDef defines a V3 token pair to check for pools
|
||||
type v3PoolDef struct {
|
||||
tokenA string
|
||||
symbolA string
|
||||
nameA string
|
||||
tokenA string
|
||||
symbolA string
|
||||
nameA string
|
||||
decimalsA int
|
||||
tokenB string
|
||||
symbolB string
|
||||
nameB string
|
||||
tokenB string
|
||||
symbolB string
|
||||
nameB string
|
||||
decimalsB int
|
||||
}
|
||||
|
||||
|
||||
+20
-20
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
// Routing constants
|
||||
const (
|
||||
DefaultMaxHops = 3
|
||||
AbsoluteMaxHops = 4
|
||||
DefaultMaxHops = 3
|
||||
AbsoluteMaxHops = 4
|
||||
MaxRoutesReturned = 5
|
||||
)
|
||||
|
||||
@@ -21,14 +21,14 @@ const LXRouterPrecompile = "0x0000000000000000000000000000000000009012"
|
||||
|
||||
// RouteHop represents a single hop in a multihop route.
|
||||
type RouteHop struct {
|
||||
TokenIn string `json:"tokenIn"`
|
||||
TokenOut string `json:"tokenOut"`
|
||||
PoolID string `json:"poolId"`
|
||||
Fee uint32 `json:"fee"`
|
||||
TickSpacing int32 `json:"tickSpacing"`
|
||||
AmountIn string `json:"amountIn"`
|
||||
AmountOut string `json:"amountOut"`
|
||||
SqrtPriceX96 string `json:"sqrtPriceX96,omitempty"`
|
||||
TokenIn string `json:"tokenIn"`
|
||||
TokenOut string `json:"tokenOut"`
|
||||
PoolID string `json:"poolId"`
|
||||
Fee uint32 `json:"fee"`
|
||||
TickSpacing int32 `json:"tickSpacing"`
|
||||
AmountIn string `json:"amountIn"`
|
||||
AmountOut string `json:"amountOut"`
|
||||
SqrtPriceX96 string `json:"sqrtPriceX96,omitempty"`
|
||||
}
|
||||
|
||||
// MultihopRoute represents a complete multihop route with gas and impact estimates.
|
||||
@@ -60,16 +60,16 @@ type RoutingResponse struct {
|
||||
// The full Pool type from types.go is used by the provider layer; this adds
|
||||
// fields specific to the routing response.
|
||||
type PoolSummary struct {
|
||||
PoolID string `json:"poolId"`
|
||||
Token0 string `json:"token0"`
|
||||
Token1 string `json:"token1"`
|
||||
Fee uint32 `json:"fee"`
|
||||
TickSpacing int32 `json:"tickSpacing"`
|
||||
TVL string `json:"tvl,omitempty"`
|
||||
Volume24h string `json:"volume24h,omitempty"`
|
||||
Liquidity string `json:"liquidity,omitempty"`
|
||||
SqrtPrice string `json:"sqrtPrice,omitempty"`
|
||||
Tick int32 `json:"tick,omitempty"`
|
||||
PoolID string `json:"poolId"`
|
||||
Token0 string `json:"token0"`
|
||||
Token1 string `json:"token1"`
|
||||
Fee uint32 `json:"fee"`
|
||||
TickSpacing int32 `json:"tickSpacing"`
|
||||
TVL string `json:"tvl,omitempty"`
|
||||
Volume24h string `json:"volume24h,omitempty"`
|
||||
Liquidity string `json:"liquidity,omitempty"`
|
||||
SqrtPrice string `json:"sqrtPrice,omitempty"`
|
||||
Tick int32 `json:"tick,omitempty"`
|
||||
}
|
||||
|
||||
// --- Validation ---
|
||||
|
||||
@@ -311,18 +311,18 @@ func TestSimulateRoute_ZeroAmount(t *testing.T) {
|
||||
func TestBuildRoutesFromPools_SortedByOutput(t *testing.T) {
|
||||
pools := []Pool{
|
||||
{
|
||||
Address: "0x0000000000000000000000000000000000001001",
|
||||
Token0: Token{Address: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
|
||||
Token1: Token{Address: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},
|
||||
Fee: 500, // Lower fee = more output
|
||||
TVL: big.NewInt(1_000_000),
|
||||
Address: "0x0000000000000000000000000000000000001001",
|
||||
Token0: Token{Address: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
|
||||
Token1: Token{Address: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},
|
||||
Fee: 500, // Lower fee = more output
|
||||
TVL: big.NewInt(1_000_000),
|
||||
},
|
||||
{
|
||||
Address: "0x0000000000000000000000000000000000001002",
|
||||
Token0: Token{Address: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
|
||||
Token1: Token{Address: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},
|
||||
Fee: 3000, // Higher fee = less output
|
||||
TVL: big.NewInt(1_000_000),
|
||||
Address: "0x0000000000000000000000000000000000001002",
|
||||
Token0: Token{Address: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
|
||||
Token1: Token{Address: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},
|
||||
Fee: 3000, // Higher fee = less output
|
||||
TVL: big.NewInt(1_000_000),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
// Keyed by orderId (UUID). Supports lookup by owner address.
|
||||
type OrderStore struct {
|
||||
mu sync.RWMutex
|
||||
orders map[string]*GatewayOrder // orderId -> order
|
||||
byAddr map[string]map[string]struct{} // lowercase(owner) -> set of orderIds
|
||||
orders map[string]*GatewayOrder // orderId -> order
|
||||
byAddr map[string]map[string]struct{} // lowercase(owner) -> set of orderIds
|
||||
}
|
||||
|
||||
// NewOrderStore creates an empty order store.
|
||||
|
||||
@@ -222,9 +222,9 @@ func TestHexAddressValidation(t *testing.T) {
|
||||
{"", false},
|
||||
{"0x", false},
|
||||
{"0x000000000000000000000000000000000000000", false}, // 39 chars
|
||||
{"0x00000000000000000000000000000000000000001", false}, // 41 chars
|
||||
{"0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", false}, // invalid hex
|
||||
{"000000000000000000000000000000000000000001", false}, // no 0x
|
||||
{"0x00000000000000000000000000000000000000001", false}, // 41 chars
|
||||
{"0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", false}, // invalid hex
|
||||
{"000000000000000000000000000000000000000001", false}, // no 0x
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -83,11 +83,11 @@ type CreateOrderRequest struct {
|
||||
|
||||
// CreateOrderResponse is returned from POST /v1/order.
|
||||
type CreateOrderResponse struct {
|
||||
OrderID string `json:"orderId"`
|
||||
Status OrderStatus `json:"status"`
|
||||
Tx *TxData `json:"tx,omitempty"` // unsigned tx for limit orders
|
||||
EIP712 *DutchOrderEIP712 `json:"eip712,omitempty"` // typed data for dutch orders
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
OrderID string `json:"orderId"`
|
||||
Status OrderStatus `json:"status"`
|
||||
Tx *TxData `json:"tx,omitempty"` // unsigned tx for limit orders
|
||||
EIP712 *DutchOrderEIP712 `json:"eip712,omitempty"` // typed data for dutch orders
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
// TxData is an unsigned transaction envelope.
|
||||
@@ -116,10 +116,10 @@ type EIP712Field struct {
|
||||
// Uses DutchOrderEIP712Domain which adds a version field to the domain separator,
|
||||
// matching the UniswapX reactor ABI.
|
||||
type DutchOrderEIP712 struct {
|
||||
Types map[string][]EIP712Field `json:"types"`
|
||||
PrimaryType string `json:"primaryType"`
|
||||
Domain DutchOrderEIP712Domain `json:"domain"`
|
||||
Message map[string]interface{} `json:"message"`
|
||||
Types map[string][]EIP712Field `json:"types"`
|
||||
PrimaryType string `json:"primaryType"`
|
||||
Domain DutchOrderEIP712Domain `json:"domain"`
|
||||
Message map[string]interface{} `json:"message"`
|
||||
}
|
||||
|
||||
// DutchOrderEIP712Domain is the domain separator for Dutch auction orders.
|
||||
|
||||
@@ -108,7 +108,7 @@ func (s *Server) handlePermit2Check(w http.ResponseWriter, r *http.Request) {
|
||||
resp := Permit2Response{
|
||||
Permit2Approved: false,
|
||||
Permit2Allowance: "0",
|
||||
SignatureRequest: sigReq,
|
||||
SignatureRequest: sigReq,
|
||||
}
|
||||
|
||||
s.writeJSON(w, http.StatusOK, resp)
|
||||
|
||||
@@ -118,11 +118,11 @@ func (s *Server) handleTradingQuote(w http.ResponseWriter, r *http.Request) {
|
||||
hops := make([]UniRouteHop, 0, len(quote.Route))
|
||||
for _, hop := range quote.Route {
|
||||
hops = append(hops, UniRouteHop{
|
||||
Type: poolTypeToUni(hop.PoolType),
|
||||
Address: hop.PoolAddress,
|
||||
TokenIn: hop.TokenIn.Address,
|
||||
Type: poolTypeToUni(hop.PoolType),
|
||||
Address: hop.PoolAddress,
|
||||
TokenIn: hop.TokenIn.Address,
|
||||
TokenOut: hop.TokenOut.Address,
|
||||
Fee: strconv.Itoa(hop.Fee),
|
||||
Fee: strconv.Itoa(hop.Fee),
|
||||
})
|
||||
}
|
||||
if len(hops) == 0 {
|
||||
|
||||
@@ -535,10 +535,10 @@ func TestBigIntStr(t *testing.T) {
|
||||
|
||||
func TestGatewayOrderToUniStatus(t *testing.T) {
|
||||
order := &GatewayOrder{
|
||||
ID: "test-123",
|
||||
Status: OrderStatusFilled,
|
||||
Owner: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
ChainID: ChainIDLux,
|
||||
ID: "test-123",
|
||||
Status: OrderStatusFilled,
|
||||
Owner: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
ChainID: ChainIDLux,
|
||||
}
|
||||
|
||||
uni := gatewayOrderToUniStatus(order)
|
||||
|
||||
@@ -9,10 +9,10 @@ import "encoding/json"
|
||||
|
||||
// UniQuoteRequest is the Uniswap-compatible quote request body.
|
||||
type UniQuoteRequest struct {
|
||||
Type string `json:"type"` // EXACT_INPUT or EXACT_OUTPUT
|
||||
Amount string `json:"amount"` // wei string
|
||||
TokenIn string `json:"tokenIn"` // address
|
||||
TokenOut string `json:"tokenOut"` // address
|
||||
Type string `json:"type"` // EXACT_INPUT or EXACT_OUTPUT
|
||||
Amount string `json:"amount"` // wei string
|
||||
TokenIn string `json:"tokenIn"` // address
|
||||
TokenOut string `json:"tokenOut"` // address
|
||||
TokenInChainId int `json:"tokenInChainId"`
|
||||
TokenOutChainId int `json:"tokenOutChainId"`
|
||||
Swapper string `json:"swapper"` // wallet address
|
||||
@@ -38,35 +38,35 @@ type UniQuoteResponse struct {
|
||||
|
||||
// UniClassicQuote is the classic (AMM) quote response body.
|
||||
type UniClassicQuote struct {
|
||||
Input UniTokenAmount `json:"input"`
|
||||
Output UniTokenAmount `json:"output"`
|
||||
Swapper string `json:"swapper"`
|
||||
Route [][]UniRouteHop `json:"route"`
|
||||
GasFee string `json:"gasFee"`
|
||||
GasFeeUSD string `json:"gasFeeUSD,omitempty"`
|
||||
GasFeeQuote string `json:"gasFeeQuote,omitempty"`
|
||||
GasUseEstimate string `json:"gasUseEstimate"`
|
||||
RouteString string `json:"routeString"`
|
||||
QuoteId string `json:"quoteId"`
|
||||
BlockNumber string `json:"blockNumber,omitempty"`
|
||||
PriceImpact float64 `json:"priceImpact,omitempty"`
|
||||
SlippageTolerance float64 `json:"slippageTolerance,omitempty"`
|
||||
TxFailureReasons []string `json:"txFailureReasons,omitempty"`
|
||||
PortionBips int `json:"portionBips,omitempty"`
|
||||
PortionAmount string `json:"portionAmount,omitempty"`
|
||||
PortionRecipient string `json:"portionRecipient,omitempty"`
|
||||
Input UniTokenAmount `json:"input"`
|
||||
Output UniTokenAmount `json:"output"`
|
||||
Swapper string `json:"swapper"`
|
||||
Route [][]UniRouteHop `json:"route"`
|
||||
GasFee string `json:"gasFee"`
|
||||
GasFeeUSD string `json:"gasFeeUSD,omitempty"`
|
||||
GasFeeQuote string `json:"gasFeeQuote,omitempty"`
|
||||
GasUseEstimate string `json:"gasUseEstimate"`
|
||||
RouteString string `json:"routeString"`
|
||||
QuoteId string `json:"quoteId"`
|
||||
BlockNumber string `json:"blockNumber,omitempty"`
|
||||
PriceImpact float64 `json:"priceImpact,omitempty"`
|
||||
SlippageTolerance float64 `json:"slippageTolerance,omitempty"`
|
||||
TxFailureReasons []string `json:"txFailureReasons,omitempty"`
|
||||
PortionBips int `json:"portionBips,omitempty"`
|
||||
PortionAmount string `json:"portionAmount,omitempty"`
|
||||
PortionRecipient string `json:"portionRecipient,omitempty"`
|
||||
}
|
||||
|
||||
// UniTokenAmount represents a token address + amount pair with chain context.
|
||||
type UniTokenAmount struct {
|
||||
Token string `json:"token"` // address
|
||||
Amount string `json:"amount"` // wei string
|
||||
Token string `json:"token"` // address
|
||||
Amount string `json:"amount"` // wei string
|
||||
ChainId int `json:"chainId"`
|
||||
}
|
||||
|
||||
// UniRouteHop represents one pool hop in a swap route.
|
||||
type UniRouteHop struct {
|
||||
Type string `json:"type"` // v2-pool, v3-pool, v4-pool
|
||||
Type string `json:"type"` // v2-pool, v3-pool, v4-pool
|
||||
Address string `json:"address"`
|
||||
TokenIn string `json:"tokenIn"`
|
||||
TokenOut string `json:"tokenOut"`
|
||||
@@ -239,9 +239,9 @@ type UniModifyLPRequest struct {
|
||||
|
||||
// UniLPResponse wraps LP operation results.
|
||||
type UniLPResponse struct {
|
||||
Type string `json:"type"` // TRANSACTION
|
||||
Type string `json:"type"` // TRANSACTION
|
||||
Tx *UniTransactionData `json:"transaction,omitempty"`
|
||||
Steps []UniLPStep `json:"steps,omitempty"` // for multi-step (approve + create)
|
||||
Steps []UniLPStep `json:"steps,omitempty"` // for multi-step (approve + create)
|
||||
}
|
||||
|
||||
// UniLPStep is one step in a multi-step LP operation.
|
||||
|
||||
@@ -56,7 +56,7 @@ func NewNativeDEXVenue(cfg NativeDEXConfig) *NativeDEXVenue {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *NativeDEXVenue) Name() string { return v.name }
|
||||
func (v *NativeDEXVenue) Name() string { return v.name }
|
||||
func (v *NativeDEXVenue) IsExecutable() bool { return true }
|
||||
|
||||
func (v *NativeDEXVenue) Quote(ctx context.Context, req VenueQuoteRequest) (*VenueQuote, error) {
|
||||
|
||||
@@ -47,7 +47,7 @@ func NewUniswapV2Venue(cfg UniswapV2Config) *UniswapV2Venue {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *UniswapV2Venue) Name() string { return v.venueName }
|
||||
func (v *UniswapV2Venue) Name() string { return v.venueName }
|
||||
func (v *UniswapV2Venue) IsExecutable() bool { return true }
|
||||
|
||||
func (v *UniswapV2Venue) Quote(ctx context.Context, req VenueQuoteRequest) (*VenueQuote, error) {
|
||||
@@ -135,7 +135,7 @@ func BuildV2SwapCalldata(routerAddress string, path []string, amountIn, amountOu
|
||||
calldata = append(calldata, selectorV2SwapExact...)
|
||||
calldata = append(calldata, lxrPadUint256(amountIn)...)
|
||||
calldata = append(calldata, lxrPadUint256(amountOutMin)...)
|
||||
calldata = append(calldata, lxrPadUint256(big.NewInt(160))...) // offset to path (5 * 32 = 160)
|
||||
calldata = append(calldata, lxrPadUint256(big.NewInt(160))...) // offset to path (5 * 32 = 160)
|
||||
calldata = append(calldata, lxrPadAddress(recipientAddr)...)
|
||||
calldata = append(calldata, lxrPadUint256(deadline)...)
|
||||
calldata = append(calldata, lxrPadUint256(big.NewInt(int64(len(path))))...) // path length
|
||||
|
||||
+8
-6
@@ -16,14 +16,16 @@ import (
|
||||
// the CPU oracle BatchEvalConstantProductCPU.
|
||||
//
|
||||
// Backend selection at compile time (CGo on):
|
||||
// darwin → amm_gpu_metal.go → Metal
|
||||
// linux → amm_gpu_cuda.go → CUDA
|
||||
// other → amm_gpu_stub.go → returns errAMMGPUUnsupported (caller falls back to CPU)
|
||||
//
|
||||
// darwin → amm_gpu_metal.go → Metal
|
||||
// linux → amm_gpu_cuda.go → CUDA
|
||||
// other → amm_gpu_stub.go → returns errAMMGPUUnsupported (caller falls back to CPU)
|
||||
//
|
||||
// Fallback policy (recorded via backend.RecordFallback):
|
||||
// GPU_DISABLE=1 → reason "disabled"
|
||||
// gpuBatchEval returns sentinel → reason "unsupported"
|
||||
// other gpuBatchEval error → surfaced to caller (no fallback)
|
||||
//
|
||||
// GPU_DISABLE=1 → reason "disabled"
|
||||
// gpuBatchEval returns sentinel → reason "unsupported"
|
||||
// other gpuBatchEval error → surfaced to caller (no fallback)
|
||||
//
|
||||
// CGo off → amm_nogpu.go provides a CPU-only implementation of this
|
||||
// entry point. One and only one BatchEvalConstantProduct symbol per build.
|
||||
|
||||
+19
-19
@@ -24,25 +24,25 @@ type SettlementHandler interface {
|
||||
type ComplianceErrorCode uint8
|
||||
|
||||
const (
|
||||
ErrNotAccredited ComplianceErrorCode = iota + 1 // Investor not accredited
|
||||
ErrJurisdiction // Investor jurisdiction blocked
|
||||
ErrHoldingPeriod // Holding period not elapsed
|
||||
ErrMaxHolders // Would exceed max holder count
|
||||
ErrKYCRequired // KYC verification missing
|
||||
ErrAMLRequired // AML clearance missing
|
||||
ErrMinNotional // Below minimum notional value
|
||||
ErrMinSize // Below minimum order size
|
||||
ErrMaxSize // Exceeds maximum order size
|
||||
ErrROFRPending // Right of first refusal pending
|
||||
ErrBoardApproval // Board approval required
|
||||
ErrTransferRestricted // Generic transfer restriction
|
||||
ErrPEPReviewRequired // PEP requires enhanced due diligence
|
||||
ErrSourceOfFundsRequired // Source of funds verification needed
|
||||
ErrAdverseMedia // Adverse media screening flagged
|
||||
ErrHighRiskCountry // FATF high-risk jurisdiction
|
||||
ErrInstitutionalOnly // Restricted to institutional investors
|
||||
ErrLockupPeriod // Asset is within lockup period
|
||||
ErrCollateralInsufficient // Insufficient collateral ratio
|
||||
ErrNotAccredited ComplianceErrorCode = iota + 1 // Investor not accredited
|
||||
ErrJurisdiction // Investor jurisdiction blocked
|
||||
ErrHoldingPeriod // Holding period not elapsed
|
||||
ErrMaxHolders // Would exceed max holder count
|
||||
ErrKYCRequired // KYC verification missing
|
||||
ErrAMLRequired // AML clearance missing
|
||||
ErrMinNotional // Below minimum notional value
|
||||
ErrMinSize // Below minimum order size
|
||||
ErrMaxSize // Exceeds maximum order size
|
||||
ErrROFRPending // Right of first refusal pending
|
||||
ErrBoardApproval // Board approval required
|
||||
ErrTransferRestricted // Generic transfer restriction
|
||||
ErrPEPReviewRequired // PEP requires enhanced due diligence
|
||||
ErrSourceOfFundsRequired // Source of funds verification needed
|
||||
ErrAdverseMedia // Adverse media screening flagged
|
||||
ErrHighRiskCountry // FATF high-risk jurisdiction
|
||||
ErrInstitutionalOnly // Restricted to institutional investors
|
||||
ErrLockupPeriod // Asset is within lockup period
|
||||
ErrCollateralInsufficient // Insufficient collateral ratio
|
||||
)
|
||||
|
||||
// ComplianceError is returned when a pre-trade check fails.
|
||||
|
||||
@@ -36,12 +36,12 @@ func retailProfile(userID string) *InvestorProfile {
|
||||
|
||||
func regD506cMarket() MarketConfig {
|
||||
return MarketConfig{
|
||||
Symbol: "ACME-USD",
|
||||
AssetClass: AssetClassEquity,
|
||||
Exemption: ExemptionRegD506c,
|
||||
ShareClass: "Series A Preferred",
|
||||
TickSize: 0.01,
|
||||
LotSize: 1.0,
|
||||
Symbol: "ACME-USD",
|
||||
AssetClass: AssetClassEquity,
|
||||
Exemption: ExemptionRegD506c,
|
||||
ShareClass: "Series A Preferred",
|
||||
TickSize: 0.01,
|
||||
LotSize: 1.0,
|
||||
MinNotional: 25000.0,
|
||||
Restrictions: &TransferRestriction{
|
||||
HoldingPeriod: 365 * 24 * time.Hour,
|
||||
|
||||
+43
-43
@@ -6,13 +6,13 @@ import "time"
|
||||
type AssetClass uint8
|
||||
|
||||
const (
|
||||
AssetClassCrypto AssetClass = iota // Unregulated digital asset
|
||||
AssetClassEquity // Common/preferred stock (domestic)
|
||||
AssetClassDebt // Bonds, notes, convertible notes
|
||||
AssetClassFund // LP interests, fund shares
|
||||
AssetClassSAFE // Simple Agreement for Future Equity
|
||||
AssetClassCommodity // Physical or digital commodity
|
||||
AssetClassDerivative // Options, futures, swaps
|
||||
AssetClassCrypto AssetClass = iota // Unregulated digital asset
|
||||
AssetClassEquity // Common/preferred stock (domestic)
|
||||
AssetClassDebt // Bonds, notes, convertible notes
|
||||
AssetClassFund // LP interests, fund shares
|
||||
AssetClassSAFE // Simple Agreement for Future Equity
|
||||
AssetClassCommodity // Physical or digital commodity
|
||||
AssetClassDerivative // Options, futures, swaps
|
||||
|
||||
// Equities — granular
|
||||
AssetClassIntlEquity // International equity (non-US)
|
||||
@@ -50,49 +50,49 @@ const (
|
||||
type ExemptionType uint8
|
||||
|
||||
const (
|
||||
ExemptionNone ExemptionType = iota // No exemption (registered or crypto)
|
||||
ExemptionNone ExemptionType = iota // No exemption (registered or crypto)
|
||||
|
||||
// United States (SEC)
|
||||
ExemptionRegD506b // Reg D Rule 506(b) — up to 35 non-accredited
|
||||
ExemptionRegD506c // Reg D Rule 506(c) — accredited only, general solicitation OK
|
||||
ExemptionRegA1 // Reg A Tier 1 — up to $20M/year
|
||||
ExemptionRegA2 // Reg A Tier 2 — up to $75M/year
|
||||
ExemptionRegCF // Regulation Crowdfunding — up to $5M/year
|
||||
ExemptionRegS // Regulation S — offshore only
|
||||
ExemptionRule144 // Rule 144 resale of restricted securities
|
||||
ExemptionSection4a7 // Section 4(a)(7) — private resales to accredited
|
||||
ExemptionRegD506b // Reg D Rule 506(b) — up to 35 non-accredited
|
||||
ExemptionRegD506c // Reg D Rule 506(c) — accredited only, general solicitation OK
|
||||
ExemptionRegA1 // Reg A Tier 1 — up to $20M/year
|
||||
ExemptionRegA2 // Reg A Tier 2 — up to $75M/year
|
||||
ExemptionRegCF // Regulation Crowdfunding — up to $5M/year
|
||||
ExemptionRegS // Regulation S — offshore only
|
||||
ExemptionRule144 // Rule 144 resale of restricted securities
|
||||
ExemptionSection4a7 // Section 4(a)(7) — private resales to accredited
|
||||
|
||||
// United Kingdom (FCA)
|
||||
ExemptionFCAPrivate // FCA private placement (no prospectus required)
|
||||
ExemptionFCAProspectus // FCA prospectus-exempt (qualified investors only)
|
||||
ExemptionFCAPrivate // FCA private placement (no prospectus required)
|
||||
ExemptionFCAProspectus // FCA prospectus-exempt (qualified investors only)
|
||||
|
||||
// Singapore (MAS)
|
||||
ExemptionMASSIP // MAS Small Offers (S$5M/12 months, <=50 persons)
|
||||
ExemptionMASPrivate // MAS Private Placement (<=50 persons in 12 months)
|
||||
ExemptionMASInstitutional // MAS Institutional Investor exemption
|
||||
ExemptionMASSIP // MAS Small Offers (S$5M/12 months, <=50 persons)
|
||||
ExemptionMASPrivate // MAS Private Placement (<=50 persons in 12 months)
|
||||
ExemptionMASInstitutional // MAS Institutional Investor exemption
|
||||
|
||||
// European Union (MiFID II / Prospectus Regulation)
|
||||
ExemptionEUQualifiedInvestor // Qualified investor exemption (Art 1(4)(a))
|
||||
ExemptionEUSmallOffering // < EUR 8M (Art 3(2))
|
||||
ExemptionEUCrowdfunding // ECSP Regulation
|
||||
ExemptionEUQualifiedInvestor // Qualified investor exemption (Art 1(4)(a))
|
||||
ExemptionEUSmallOffering // < EUR 8M (Art 3(2))
|
||||
ExemptionEUCrowdfunding // ECSP Regulation
|
||||
|
||||
// Hong Kong (SFO)
|
||||
ExemptionHKProfessionalInvestor // Professional investor (Cap 571D)
|
||||
ExemptionHKSmallOffering // Offers to <50 persons
|
||||
ExemptionHKProfessionalInvestor // Professional investor (Cap 571D)
|
||||
ExemptionHKSmallOffering // Offers to <50 persons
|
||||
|
||||
// Japan (FIEA)
|
||||
ExemptionJPQII // Qualified Institutional Investor
|
||||
ExemptionJPQII // Qualified Institutional Investor
|
||||
|
||||
// Australia (Corporations Act)
|
||||
ExemptionAUSophisticated // Sophisticated investor (s708(8))
|
||||
ExemptionAUSmallScale // Small scale offering (s708(1))
|
||||
ExemptionAUSophisticated // Sophisticated investor (s708(8))
|
||||
ExemptionAUSmallScale // Small scale offering (s708(1))
|
||||
|
||||
// UAE (DFSA / ADGM)
|
||||
ExemptionUAEExemptOffer // Exempt offer under DFSA Rules
|
||||
ExemptionUAEQualifiedInvestor // Qualified investor under ADGM
|
||||
ExemptionUAEExemptOffer // Exempt offer under DFSA Rules
|
||||
ExemptionUAEQualifiedInvestor // Qualified investor under ADGM
|
||||
|
||||
// Switzerland (FinSA)
|
||||
ExemptionCHQualifiedInvestor // Qualified investor exemption
|
||||
ExemptionCHQualifiedInvestor // Qualified investor exemption
|
||||
)
|
||||
|
||||
// InvestorStatus represents the regulatory classification of an investor.
|
||||
@@ -100,13 +100,13 @@ const (
|
||||
type InvestorStatus uint8
|
||||
|
||||
const (
|
||||
InvestorRetail InvestorStatus = iota // Non-accredited retail
|
||||
InvestorAccredited // US: SEC accredited investor
|
||||
InvestorSophisticated // UK: FCA certified sophisticated investor
|
||||
InvestorProfessional // UK: FCA professional client / SG: MAS accredited
|
||||
InvestorQualifiedPurchaser // US: Qualified purchaser ($5M+ investments)
|
||||
InvestorQualifiedClient // US: Qualified client ($1.1M+ net worth)
|
||||
InvestorInstitutional // Institutional investor (bank, fund, etc.)
|
||||
InvestorRetail InvestorStatus = iota // Non-accredited retail
|
||||
InvestorAccredited // US: SEC accredited investor
|
||||
InvestorSophisticated // UK: FCA certified sophisticated investor
|
||||
InvestorProfessional // UK: FCA professional client / SG: MAS accredited
|
||||
InvestorQualifiedPurchaser // US: Qualified purchaser ($5M+ investments)
|
||||
InvestorQualifiedClient // US: Qualified client ($1.1M+ net worth)
|
||||
InvestorInstitutional // Institutional investor (bank, fund, etc.)
|
||||
)
|
||||
|
||||
// TransferRestriction defines a constraint on the transferability of a security.
|
||||
@@ -140,10 +140,10 @@ type TransferRestriction struct {
|
||||
type PEPStatus uint8
|
||||
|
||||
const (
|
||||
PEPNone PEPStatus = iota // Not a PEP
|
||||
PEPDirect // Directly a PEP (head of state, senior official, etc.)
|
||||
PEPRelated // Close associate or family member of a PEP
|
||||
PEPFormer // Former PEP (cooling-off period)
|
||||
PEPNone PEPStatus = iota // Not a PEP
|
||||
PEPDirect // Directly a PEP (head of state, senior official, etc.)
|
||||
PEPRelated // Close associate or family member of a PEP
|
||||
PEPFormer // Former PEP (cooling-off period)
|
||||
)
|
||||
|
||||
// InvestorProfile holds the compliance-relevant attributes of a trader.
|
||||
|
||||
+57
-57
@@ -29,30 +29,30 @@ const (
|
||||
type FundsRoute uint8
|
||||
|
||||
const (
|
||||
RouteDEXInternal FundsRoute = iota // DEX → same DEX (internal: margin, vault, lending)
|
||||
RouteCEXInternal // CEX → same CEX (account transfer, margin)
|
||||
RouteDEXToDEX // DEX → different DEX (cross-chain bridge)
|
||||
RouteCEXToCEX // CEX → different CEX (broker transfer)
|
||||
RouteDEXToCEX // DEX → CEX (on-chain withdraw → custodial deposit)
|
||||
RouteCEXToDEX // CEX → DEX (custodial withdraw → on-chain deposit)
|
||||
RouteExternalIn // Fiat/external → venue (wire, ACH, card)
|
||||
RouteExternalOut // Venue → fiat/external (wire out)
|
||||
RouteDEXInternal FundsRoute = iota // DEX → same DEX (internal: margin, vault, lending)
|
||||
RouteCEXInternal // CEX → same CEX (account transfer, margin)
|
||||
RouteDEXToDEX // DEX → different DEX (cross-chain bridge)
|
||||
RouteCEXToCEX // CEX → different CEX (broker transfer)
|
||||
RouteDEXToCEX // DEX → CEX (on-chain withdraw → custodial deposit)
|
||||
RouteCEXToDEX // CEX → DEX (custodial withdraw → on-chain deposit)
|
||||
RouteExternalIn // Fiat/external → venue (wire, ACH, card)
|
||||
RouteExternalOut // Venue → fiat/external (wire out)
|
||||
)
|
||||
|
||||
// FundsStatus tracks the lifecycle of a funds movement.
|
||||
type FundsStatus uint8
|
||||
|
||||
const (
|
||||
FundsStatusPending FundsStatus = iota // Created, awaiting processing
|
||||
FundsStatusCompliance // Undergoing compliance review
|
||||
FundsStatusApproved // Compliance approved, ready to execute
|
||||
FundsStatusProcessing // Execution in progress
|
||||
FundsStatusConfirming // Awaiting on-chain confirmations (DEX)
|
||||
FundsStatusSettling // Settlement in progress (CEX T+1/T+2)
|
||||
FundsStatusCompleted // Fully settled
|
||||
FundsStatusFailed // Failed (insufficient funds, compliance, etc.)
|
||||
FundsStatusCancelled // Cancelled by user or system
|
||||
FundsStatusFrozen // Frozen by compliance (AML hold)
|
||||
FundsStatusPending FundsStatus = iota // Created, awaiting processing
|
||||
FundsStatusCompliance // Undergoing compliance review
|
||||
FundsStatusApproved // Compliance approved, ready to execute
|
||||
FundsStatusProcessing // Execution in progress
|
||||
FundsStatusConfirming // Awaiting on-chain confirmations (DEX)
|
||||
FundsStatusSettling // Settlement in progress (CEX T+1/T+2)
|
||||
FundsStatusCompleted // Fully settled
|
||||
FundsStatusFailed // Failed (insufficient funds, compliance, etc.)
|
||||
FundsStatusCancelled // Cancelled by user or system
|
||||
FundsStatusFrozen // Frozen by compliance (AML hold)
|
||||
)
|
||||
|
||||
// RiskLevel classifies the risk of a funds movement.
|
||||
@@ -83,16 +83,16 @@ type FundsMovement struct {
|
||||
DestChain string // For DEX: chain ID. For CEX: empty.
|
||||
|
||||
// Asset
|
||||
Asset string // Symbol (e.g. "USDC", "BTC", "XAU")
|
||||
Amount *big.Int // In smallest unit (wei, satoshi, etc.)
|
||||
Fee *big.Int // Total fee charged
|
||||
Asset string // Symbol (e.g. "USDC", "BTC", "XAU")
|
||||
Amount *big.Int // In smallest unit (wei, satoshi, etc.)
|
||||
Fee *big.Int // Total fee charged
|
||||
NetAmount *big.Int // Amount after fee
|
||||
|
||||
// Status
|
||||
Status FundsStatus
|
||||
Risk RiskLevel
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Status FundsStatus
|
||||
Risk RiskLevel
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
CompletedAt *time.Time
|
||||
|
||||
// Compliance
|
||||
@@ -103,10 +103,10 @@ type FundsMovement struct {
|
||||
ReviewerID string // Manual reviewer (if required)
|
||||
|
||||
// On-chain (DEX movements)
|
||||
SourceTxHash string // Source chain transaction
|
||||
DestTxHash string // Destination chain transaction
|
||||
Confirmations int // Current confirmations
|
||||
RequiredConfs int // Required confirmations
|
||||
SourceTxHash string // Source chain transaction
|
||||
DestTxHash string // Destination chain transaction
|
||||
Confirmations int // Current confirmations
|
||||
RequiredConfs int // Required confirmations
|
||||
|
||||
// Settlement (CEX movements)
|
||||
SettlementType string // instant, t1, t2, wire
|
||||
@@ -121,11 +121,11 @@ type FundsMovement struct {
|
||||
|
||||
// RiskProfile describes the risk characteristics of a specific route.
|
||||
type RiskProfile struct {
|
||||
Route FundsRoute
|
||||
CounterpartyRisk string // "none" (DEX), "custodial" (CEX), "bridge" (cross-chain)
|
||||
SmartContractRisk bool // True for DEX movements
|
||||
CustodialRisk bool // True for CEX movements
|
||||
BridgeRisk bool // True for cross-chain
|
||||
Route FundsRoute
|
||||
CounterpartyRisk string // "none" (DEX), "custodial" (CEX), "bridge" (cross-chain)
|
||||
SmartContractRisk bool // True for DEX movements
|
||||
CustodialRisk bool // True for CEX movements
|
||||
BridgeRisk bool // True for cross-chain
|
||||
SettlementTime string // "instant", "~15min", "T+1", "T+2", "1-5 business days"
|
||||
ComplianceGates []string
|
||||
TypicalFees string // Human-readable fee description
|
||||
@@ -134,8 +134,8 @@ type RiskProfile struct {
|
||||
// routeRiskProfiles defines the risk characteristics for each route.
|
||||
var routeRiskProfiles = map[FundsRoute]RiskProfile{
|
||||
RouteDEXInternal: {
|
||||
Route: RouteDEXInternal,
|
||||
CounterpartyRisk: "none",
|
||||
Route: RouteDEXInternal,
|
||||
CounterpartyRisk: "none",
|
||||
SmartContractRisk: true,
|
||||
SettlementTime: "instant",
|
||||
ComplianceGates: []string{},
|
||||
@@ -145,13 +145,13 @@ var routeRiskProfiles = map[FundsRoute]RiskProfile{
|
||||
Route: RouteCEXInternal,
|
||||
CounterpartyRisk: "custodial",
|
||||
CustodialRisk: true,
|
||||
SettlementTime: "instant",
|
||||
ComplianceGates: []string{"account_verification"},
|
||||
TypicalFees: "none",
|
||||
SettlementTime: "instant",
|
||||
ComplianceGates: []string{"account_verification"},
|
||||
TypicalFees: "none",
|
||||
},
|
||||
RouteDEXToDEX: {
|
||||
Route: RouteDEXToDEX,
|
||||
CounterpartyRisk: "bridge",
|
||||
Route: RouteDEXToDEX,
|
||||
CounterpartyRisk: "bridge",
|
||||
SmartContractRisk: true,
|
||||
BridgeRisk: true,
|
||||
SettlementTime: "~15min (bridge confirmations)",
|
||||
@@ -162,13 +162,13 @@ var routeRiskProfiles = map[FundsRoute]RiskProfile{
|
||||
Route: RouteCEXToCEX,
|
||||
CounterpartyRisk: "custodial",
|
||||
CustodialRisk: true,
|
||||
SettlementTime: "T+1 to T+2",
|
||||
ComplianceGates: []string{"kyc", "aml", "withdrawal_limit"},
|
||||
TypicalFees: "broker transfer fee",
|
||||
SettlementTime: "T+1 to T+2",
|
||||
ComplianceGates: []string{"kyc", "aml", "withdrawal_limit"},
|
||||
TypicalFees: "broker transfer fee",
|
||||
},
|
||||
RouteDEXToCEX: {
|
||||
Route: RouteDEXToCEX,
|
||||
CounterpartyRisk: "custodial",
|
||||
Route: RouteDEXToCEX,
|
||||
CounterpartyRisk: "custodial",
|
||||
SmartContractRisk: true,
|
||||
CustodialRisk: true,
|
||||
SettlementTime: "~30min (on-chain confirm + CEX credit)",
|
||||
@@ -176,9 +176,9 @@ var routeRiskProfiles = map[FundsRoute]RiskProfile{
|
||||
TypicalFees: "gas + CEX deposit fee (often free)",
|
||||
},
|
||||
RouteCEXToDEX: {
|
||||
Route: RouteCEXToDEX,
|
||||
CounterpartyRisk: "custodial",
|
||||
CustodialRisk: true,
|
||||
Route: RouteCEXToDEX,
|
||||
CounterpartyRisk: "custodial",
|
||||
CustodialRisk: true,
|
||||
SmartContractRisk: true,
|
||||
SettlementTime: "~15min (CEX processing + on-chain confirm)",
|
||||
ComplianceGates: []string{"kyc", "aml", "withdrawal_limit", "address_whitelist", "2fa"},
|
||||
@@ -188,17 +188,17 @@ var routeRiskProfiles = map[FundsRoute]RiskProfile{
|
||||
Route: RouteExternalIn,
|
||||
CounterpartyRisk: "banking",
|
||||
CustodialRisk: true,
|
||||
SettlementTime: "1-5 business days (wire/ACH)",
|
||||
ComplianceGates: []string{"kyc", "aml", "source_of_funds", "bank_verification"},
|
||||
TypicalFees: "wire fee ($25-50) or free (ACH)",
|
||||
SettlementTime: "1-5 business days (wire/ACH)",
|
||||
ComplianceGates: []string{"kyc", "aml", "source_of_funds", "bank_verification"},
|
||||
TypicalFees: "wire fee ($25-50) or free (ACH)",
|
||||
},
|
||||
RouteExternalOut: {
|
||||
Route: RouteExternalOut,
|
||||
CounterpartyRisk: "banking",
|
||||
CustodialRisk: true,
|
||||
SettlementTime: "1-5 business days (wire/ACH)",
|
||||
ComplianceGates: []string{"kyc", "aml", "withdrawal_limit", "bank_verification", "travel_rule"},
|
||||
TypicalFees: "wire fee ($25-50) or free (ACH)",
|
||||
SettlementTime: "1-5 business days (wire/ACH)",
|
||||
ComplianceGates: []string{"kyc", "aml", "withdrawal_limit", "bank_verification", "travel_rule"},
|
||||
TypicalFees: "wire fee ($25-50) or free (ACH)",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -223,8 +223,8 @@ type FundsManager struct {
|
||||
lastReset time.Time
|
||||
|
||||
// Hooks
|
||||
preCheck func(m *FundsMovement) error // Pre-movement compliance check
|
||||
onComplete func(m *FundsMovement) // Post-settlement callback
|
||||
preCheck func(m *FundsMovement) error // Pre-movement compliance check
|
||||
onComplete func(m *FundsMovement) // Post-settlement callback
|
||||
onFail func(m *FundsMovement, reason string)
|
||||
|
||||
// Bridge for cross-chain movements
|
||||
|
||||
@@ -48,8 +48,8 @@ type MarketConfig struct {
|
||||
InstitutionalOnly bool // Institutions only (QIB, etc.)
|
||||
|
||||
// CEX reconciliation — links this DEX market to a CEX venue
|
||||
CEXSymbol string // Corresponding CEX symbol (if reconciled)
|
||||
CEXVenue string // CEX venue identifier (e.g. "lux-cex")
|
||||
CEXSymbol string // Corresponding CEX symbol (if reconciled)
|
||||
CEXVenue string // CEX venue identifier (e.g. "lux-cex")
|
||||
ReconciliationMode ReconciliationMode // How cross-venue settlement works
|
||||
}
|
||||
|
||||
@@ -77,10 +77,10 @@ func DefaultMarketConfig() MarketConfig {
|
||||
// FixedIncomeMarketConfig returns a config for bond/note markets.
|
||||
func FixedIncomeMarketConfig(symbol string) MarketConfig {
|
||||
return MarketConfig{
|
||||
Symbol: symbol,
|
||||
AssetClass: AssetClassFixedIncome,
|
||||
TickSize: 0.001,
|
||||
LotSize: 1000, // Standard bond lot
|
||||
Symbol: symbol,
|
||||
AssetClass: AssetClassFixedIncome,
|
||||
TickSize: 0.001,
|
||||
LotSize: 1000, // Standard bond lot
|
||||
MinNotional: 1000,
|
||||
}
|
||||
}
|
||||
@@ -136,8 +136,8 @@ func PrivateEquityMarketConfig(symbol string) MarketConfig {
|
||||
InstitutionalOnly: false,
|
||||
SingleMatchMode: true, // Private secondaries
|
||||
Restrictions: &TransferRestriction{
|
||||
MinInvestorStatus: InvestorQualifiedPurchaser,
|
||||
ROFRRequired: true,
|
||||
MinInvestorStatus: InvestorQualifiedPurchaser,
|
||||
ROFRRequired: true,
|
||||
BoardApprovalRequired: true,
|
||||
},
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ var MaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewI
|
||||
|
||||
// Math errors matching Solidity revert semantics.
|
||||
var (
|
||||
ErrDivisionByZero = errors.New("division by zero")
|
||||
ErrDivisionByZero = errors.New("division by zero")
|
||||
ErrUint256Overflow = errors.New("uint256 overflow")
|
||||
)
|
||||
|
||||
|
||||
@@ -21,9 +21,10 @@ import (
|
||||
// linked-list semantics and is NOT touched by this dispatch.
|
||||
//
|
||||
// Fallback policy (recorded via backend.RecordFallback):
|
||||
// GPU_DISABLE=1 → reason "disabled"
|
||||
// gpuMatchOrder returns sentinel → reason "unsupported"
|
||||
// other gpuMatchOrder error → surfaced to caller (no fallback)
|
||||
//
|
||||
// GPU_DISABLE=1 → reason "disabled"
|
||||
// gpuMatchOrder returns sentinel → reason "unsupported"
|
||||
// other gpuMatchOrder error → surfaced to caller (no fallback)
|
||||
//
|
||||
// Returns: trades emitted, remaining quantity on the incoming order.
|
||||
// `book` may be mutated in place to reflect updated Remaining / Status.
|
||||
|
||||
+10
-10
@@ -21,10 +21,10 @@ type (
|
||||
|
||||
// Pool fee tiers (basis points)
|
||||
const (
|
||||
Fee001 uint24 = 100 // 0.01% - stablecoins
|
||||
Fee005 uint24 = 500 // 0.05% - stable pairs
|
||||
Fee030 uint24 = 3000 // 0.30% - standard
|
||||
Fee100 uint24 = 10000 // 1.00% - exotic pairs
|
||||
Fee001 uint24 = 100 // 0.01% - stablecoins
|
||||
Fee005 uint24 = 500 // 0.05% - stable pairs
|
||||
Fee030 uint24 = 3000 // 0.30% - standard
|
||||
Fee100 uint24 = 10000 // 1.00% - exotic pairs
|
||||
FeeMax uint24 = 100000 // 10% max fee
|
||||
)
|
||||
|
||||
@@ -218,12 +218,12 @@ func NewTickBitmap() *TickBitmap {
|
||||
// PoolState extends Pool with V4 tick-level state for concentrated liquidity.
|
||||
type PoolState struct {
|
||||
*Pool
|
||||
Ticks map[int32]*TickInfo // Per-tick state
|
||||
TickBitmap *TickBitmap // Initialized tick tracking
|
||||
Positions map[[32]byte]*LPPosition // Position states
|
||||
TickSpacing int32 // From pool key
|
||||
LPFee uint32 // LP fee in pips
|
||||
ProtocolFee uint32 // Protocol fee in pips
|
||||
Ticks map[int32]*TickInfo // Per-tick state
|
||||
TickBitmap *TickBitmap // Initialized tick tracking
|
||||
Positions map[[32]byte]*LPPosition // Position states
|
||||
TickSpacing int32 // From pool key
|
||||
LPFee uint32 // LP fee in pips
|
||||
ProtocolFee uint32 // Protocol fee in pips
|
||||
}
|
||||
|
||||
// NewPoolState wraps a Pool with V4 tick-level state.
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
// helper to build big.Ints concisely
|
||||
func bi(v int64) *big.Int { return big.NewInt(v) }
|
||||
func bis(s string) *big.Int { v, _ := new(big.Int).SetString(s, 10); return v }
|
||||
func bi(v int64) *big.Int { return big.NewInt(v) }
|
||||
func bis(s string) *big.Int { v, _ := new(big.Int).SetString(s, 10); return v }
|
||||
|
||||
func TestSqrtPriceMath_GetNextSqrtPriceFromAmount0RoundingUp(t *testing.T) {
|
||||
// Use a realistic sqrtPrice ~= sqrt(1) * 2^96 = 2^96
|
||||
@@ -161,9 +161,9 @@ func TestSqrtPriceMath_GetNextSqrtPriceFromOutput(t *testing.T) {
|
||||
|
||||
func TestSqrtPriceMath_GetAmount0Delta(t *testing.T) {
|
||||
// Two prices: sqrtPrice at tick 0 (1:1) and at a higher price
|
||||
priceLow := new(big.Int).Set(Q96) // sqrt(1) * 2^96
|
||||
priceHigh := new(big.Int).Mul(Q96, bi(2)) // sqrt(4) * 2^96 = 2 * Q96
|
||||
liquidity := bis("1000000000000000000") // 1e18
|
||||
priceLow := new(big.Int).Set(Q96) // sqrt(1) * 2^96
|
||||
priceHigh := new(big.Int).Mul(Q96, bi(2)) // sqrt(4) * 2^96 = 2 * Q96
|
||||
liquidity := bis("1000000000000000000") // 1e18
|
||||
|
||||
t.Run("roundUp >= roundDown", func(t *testing.T) {
|
||||
up, err := GetAmount0Delta(priceLow, priceHigh, liquidity, true)
|
||||
|
||||
+33
-33
@@ -20,12 +20,12 @@ const (
|
||||
type ReconciliationStatus uint8
|
||||
|
||||
const (
|
||||
ReconPending ReconciliationStatus = iota // Awaiting counterpart
|
||||
ReconMatched // Both sides matched
|
||||
ReconSettling // Settlement in progress
|
||||
ReconSettled // Fully settled
|
||||
ReconFailed // Settlement failed
|
||||
ReconDisputed // Under dispute
|
||||
ReconPending ReconciliationStatus = iota // Awaiting counterpart
|
||||
ReconMatched // Both sides matched
|
||||
ReconSettling // Settlement in progress
|
||||
ReconSettled // Fully settled
|
||||
ReconFailed // Settlement failed
|
||||
ReconDisputed // Under dispute
|
||||
)
|
||||
|
||||
// CrossVenueTrade represents a trade that may span CEX and DEX.
|
||||
@@ -40,10 +40,10 @@ type CrossVenueTrade struct {
|
||||
DEXTxHash string // on-chain transaction hash
|
||||
|
||||
// CEX side
|
||||
CEXOrderID string // CEX uses string UUIDs
|
||||
CEXTradeID string
|
||||
CEXSymbol string
|
||||
CEXVenue string // execution venue on CEX
|
||||
CEXOrderID string // CEX uses string UUIDs
|
||||
CEXTradeID string
|
||||
CEXSymbol string
|
||||
CEXVenue string // execution venue on CEX
|
||||
CEXComplianceID string
|
||||
|
||||
// Common fields
|
||||
@@ -61,7 +61,7 @@ type CrossVenueTrade struct {
|
||||
FailureReason string
|
||||
|
||||
// Regulatory
|
||||
Jurisdiction string // Governing jurisdiction
|
||||
Jurisdiction string // Governing jurisdiction
|
||||
ReportedToRegulator bool
|
||||
RegulatoryReportID string
|
||||
}
|
||||
@@ -69,29 +69,29 @@ type CrossVenueTrade struct {
|
||||
// dexToCEXMap maps DEX asset classes to CEX string equivalents.
|
||||
// Used by DEXToCEXAssetClass — every DEX class has exactly one CEX mapping.
|
||||
var dexToCEXMap = map[AssetClass]string{
|
||||
AssetClassCrypto: "crypto",
|
||||
AssetClassEquity: "us_equity",
|
||||
AssetClassIntlEquity: "intl_equity",
|
||||
AssetClassDebt: "fixed_income",
|
||||
AssetClassFund: "venture",
|
||||
AssetClassSAFE: "venture",
|
||||
AssetClassCommodity: "commodities",
|
||||
AssetClassDerivative: "options",
|
||||
AssetClassForex: "forex",
|
||||
AssetClassOptions: "options",
|
||||
AssetClassFutures: "futures",
|
||||
AssetClassFixedIncome: "fixed_income",
|
||||
AssetClassMunicipal: "municipal",
|
||||
AssetClassStructured: "structured",
|
||||
AssetClassCorporateDebt: "corporate_debt",
|
||||
AssetClassConsumerDebt: "consumer_debt",
|
||||
AssetClassRealEstate: "real_estate",
|
||||
AssetClassCrypto: "crypto",
|
||||
AssetClassEquity: "us_equity",
|
||||
AssetClassIntlEquity: "intl_equity",
|
||||
AssetClassDebt: "fixed_income",
|
||||
AssetClassFund: "venture",
|
||||
AssetClassSAFE: "venture",
|
||||
AssetClassCommodity: "commodities",
|
||||
AssetClassDerivative: "options",
|
||||
AssetClassForex: "forex",
|
||||
AssetClassOptions: "options",
|
||||
AssetClassFutures: "futures",
|
||||
AssetClassFixedIncome: "fixed_income",
|
||||
AssetClassMunicipal: "municipal",
|
||||
AssetClassStructured: "structured",
|
||||
AssetClassCorporateDebt: "corporate_debt",
|
||||
AssetClassConsumerDebt: "consumer_debt",
|
||||
AssetClassRealEstate: "real_estate",
|
||||
AssetClassPreciousMetals: "precious_metals",
|
||||
AssetClassPrivateEquity: "private_equity",
|
||||
AssetClassVenture: "venture",
|
||||
AssetClassPrivateCredit: "private_credit",
|
||||
AssetClassCDP: "cdp",
|
||||
AssetClassLP: "lp",
|
||||
AssetClassPrivateEquity: "private_equity",
|
||||
AssetClassVenture: "venture",
|
||||
AssetClassPrivateCredit: "private_credit",
|
||||
AssetClassCDP: "cdp",
|
||||
AssetClassLP: "lp",
|
||||
}
|
||||
|
||||
// cexToDEXMap maps CEX string asset classes to their most specific DEX equivalent.
|
||||
|
||||
+1
-1
@@ -608,7 +608,7 @@ func updatePosition(
|
||||
if !ok {
|
||||
pos = &LPPosition{
|
||||
Owner: owner, TickLower: params.TickLower, TickUpper: params.TickUpper,
|
||||
Liquidity: big.NewInt(0),
|
||||
Liquidity: big.NewInt(0),
|
||||
FeeGrowthInside0LastX128: big.NewInt(0), FeeGrowthInside1LastX128: big.NewInt(0),
|
||||
TokensOwed0: big.NewInt(0), TokensOwed1: big.NewInt(0),
|
||||
}
|
||||
|
||||
+7
-8
@@ -71,7 +71,6 @@ const (
|
||||
VenueTypeTraderJoe = "traderjoe"
|
||||
)
|
||||
|
||||
|
||||
// --- Request types ---
|
||||
|
||||
// QuoteRequest is the input for POST /v1/trade/quote.
|
||||
@@ -189,13 +188,13 @@ type SwapStatusResponse struct {
|
||||
|
||||
// OrderStatusResponse is returned by GET /v1/trade/orders/:orderId.
|
||||
type OrderStatusResponse struct {
|
||||
OrderID string `json:"orderId"`
|
||||
Status string `json:"status"`
|
||||
FilledQty string `json:"filledQty"`
|
||||
AvgPrice string `json:"avgPrice"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Venue string `json:"venue"`
|
||||
OrderID string `json:"orderId"`
|
||||
Status string `json:"status"`
|
||||
FilledQty string `json:"filledQty"`
|
||||
AvgPrice string `json:"avgPrice"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Venue string `json:"venue"`
|
||||
}
|
||||
|
||||
// ApprovalCheckResponse is returned by POST /v1/trade/check-approval.
|
||||
|
||||
@@ -117,7 +117,7 @@ func (r *CrossChainRouter) bfsRoutes(ctx context.Context, req CrossChainQuoteReq
|
||||
start := &bfsNode{
|
||||
chainID: req.SourceChain, token: req.TokenIn,
|
||||
amount: req.Amount, originalInput: req.Amount,
|
||||
chains: []int{req.SourceChain},
|
||||
chains: []int{req.SourceChain},
|
||||
totalGas: big.NewInt(0), totalFee: big.NewInt(0),
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// Test addresses — generic, not chain-specific.
|
||||
const (
|
||||
testLUSD = "0xc65ea8882020Af7CDa7854d590C6Fcd34BF364ec"
|
||||
testLUSD = "0xc65ea8882020Af7CDa7854d590C6Fcd34BF364ec"
|
||||
testWBTC = "0x1234567890abcdef1234567890abcdef12345678"
|
||||
testWETH = "0xabcdef1234567890abcdef1234567890abcdef12"
|
||||
testUser = "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714"
|
||||
@@ -1141,9 +1141,9 @@ func TestBrokerHTTPVenueNoSymbol(t *testing.T) {
|
||||
|
||||
// No symbol registered for this pair → nil quote.
|
||||
quote, err := venue.Quote(context.Background(), QuoteRequest{
|
||||
TokenIn: "0x0000000000000000000000000000000000000099",
|
||||
TokenIn: "0x0000000000000000000000000000000000000099",
|
||||
TokenOut: "0x0000000000000000000000000000000000000098",
|
||||
Amount: "1000", Type: QuoteTypeExactInput,
|
||||
Amount: "1000", Type: QuoteTypeExactInput,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
@@ -1159,10 +1159,10 @@ func TestBrokerHTTPVenueNoSymbol(t *testing.T) {
|
||||
|
||||
func TestVenueTypeClassification(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
isV2 bool
|
||||
isV3 bool
|
||||
isV4 bool
|
||||
name string
|
||||
isV2 bool
|
||||
isV3 bool
|
||||
isV4 bool
|
||||
}{
|
||||
{"v4_native", false, false, true},
|
||||
{"v4_native_clob", false, false, true},
|
||||
|
||||
@@ -61,8 +61,8 @@ func (v *V4Venue) AddPool(tokenIn, tokenOut string, poolID string, feeBPS int, r
|
||||
}
|
||||
}
|
||||
|
||||
func (v *V4Venue) Name() string { return "v4_native" }
|
||||
func (v *V4Venue) IsExecutable() bool { return true }
|
||||
func (v *V4Venue) Name() string { return "v4_native" }
|
||||
func (v *V4Venue) IsExecutable() bool { return true }
|
||||
|
||||
func (v *V4Venue) Quote(_ context.Context, req QuoteRequest) (*VenueQuote, error) {
|
||||
key := req.TokenIn + ":" + req.TokenOut
|
||||
@@ -166,8 +166,8 @@ func (b *BrokerVenue) SetFees(spreadBPS, feeBPS int) {
|
||||
b.feeBPS = feeBPS
|
||||
}
|
||||
|
||||
func (b *BrokerVenue) Name() string { return b.provider }
|
||||
func (b *BrokerVenue) IsExecutable() bool { return false }
|
||||
func (b *BrokerVenue) Name() string { return b.provider }
|
||||
func (b *BrokerVenue) IsExecutable() bool { return false }
|
||||
|
||||
func (b *BrokerVenue) Quote(_ context.Context, req QuoteRequest) (*VenueQuote, error) {
|
||||
if b.mockPrice == nil || b.mockPrice.Sign() == 0 {
|
||||
|
||||
@@ -17,7 +17,7 @@ type percentageFeeBridge struct {
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
func (b *percentageFeeBridge) BridgeType() BridgeType { return b.bridgeType }
|
||||
func (b *percentageFeeBridge) BridgeType() BridgeType { return b.bridgeType }
|
||||
func (b *percentageFeeBridge) SupportedRoutes() [][2]int { return b.routes }
|
||||
func (b *percentageFeeBridge) SupportedTokens(src, _ int) []string {
|
||||
return b.tokens[src]
|
||||
|
||||
@@ -97,7 +97,7 @@ func NewBrokerHTTPVenue(cfg BrokerHTTPConfig) *BrokerHTTPVenue {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *BrokerHTTPVenue) Name() string { return v.provider }
|
||||
func (v *BrokerHTTPVenue) Name() string { return v.provider }
|
||||
func (v *BrokerHTTPVenue) IsExecutable() bool { return false }
|
||||
|
||||
func (v *BrokerHTTPVenue) Quote(ctx context.Context, req QuoteRequest) (*VenueQuote, error) {
|
||||
@@ -198,7 +198,7 @@ func NewBrokerSORVenue(brokerURL, apiKey string, registry *SymbolRegistry) *Brok
|
||||
}
|
||||
}
|
||||
|
||||
func (v *BrokerSORVenue) Name() string { return "broker_sor" }
|
||||
func (v *BrokerSORVenue) Name() string { return "broker_sor" }
|
||||
func (v *BrokerSORVenue) IsExecutable() bool { return false }
|
||||
|
||||
func (v *BrokerSORVenue) Quote(ctx context.Context, req QuoteRequest) (*VenueQuote, error) {
|
||||
|
||||
@@ -44,7 +44,7 @@ func NewNativeDEXVenue(cfg NativeDEXConfig) *NativeDEXVenue {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *NativeDEXVenue) Name() string { return v.name }
|
||||
func (v *NativeDEXVenue) Name() string { return v.name }
|
||||
func (v *NativeDEXVenue) IsExecutable() bool { return true }
|
||||
|
||||
func (v *NativeDEXVenue) Quote(ctx context.Context, req QuoteRequest) (*VenueQuote, error) {
|
||||
@@ -138,4 +138,3 @@ func (v *NativeDEXVenue) queryCLOB(ctx context.Context, req QuoteRequest, amount
|
||||
Executable: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ func NewUniswapV2Venue(cfg UniswapV2Config) *UniswapV2Venue {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *UniswapV2Venue) Name() string { return v.venueName }
|
||||
func (v *UniswapV2Venue) Name() string { return v.venueName }
|
||||
func (v *UniswapV2Venue) IsExecutable() bool { return true }
|
||||
|
||||
func (v *UniswapV2Venue) Quote(ctx context.Context, req QuoteRequest) (*VenueQuote, error) {
|
||||
|
||||
@@ -51,7 +51,7 @@ func NewUniswapV3Venue(cfg UniswapV3Config) *UniswapV3Venue {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *UniswapV3Venue) Name() string { return v.venueName }
|
||||
func (v *UniswapV3Venue) Name() string { return v.venueName }
|
||||
func (v *UniswapV3Venue) IsExecutable() bool { return true }
|
||||
|
||||
func (v *UniswapV3Venue) Quote(ctx context.Context, req QuoteRequest) (*VenueQuote, error) {
|
||||
|
||||
+5
-5
@@ -431,10 +431,10 @@ func TestDEX_ConcurrentCancelFill(t *testing.T) {
|
||||
require.NotZero(t, sellID)
|
||||
|
||||
var (
|
||||
cancelErr error
|
||||
trades []lx.Trade
|
||||
cancelDone = make(chan struct{})
|
||||
matchDone = make(chan struct{})
|
||||
cancelErr error
|
||||
trades []lx.Trade
|
||||
cancelDone = make(chan struct{})
|
||||
matchDone = make(chan struct{})
|
||||
)
|
||||
|
||||
// Race: cancel vs match
|
||||
@@ -557,7 +557,7 @@ func TestDEX_GatewayLoadShedding(t *testing.T) {
|
||||
ob := lx.NewOrderBook("LOAD-USD")
|
||||
|
||||
const (
|
||||
numWorkers = 100
|
||||
numWorkers = 100
|
||||
ordersPerWorker = 100 // 10K total
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user