Placing dShare Orders
Before an Entity
can place a dShare order, make sure it fulfills the following pre-requisites:
- Entity has a valid KYC
- Entity has a valid account
- Entity has enough funds
Ordering using the API SDK
This is only usable by Accounts with a Dinari-managed wallet
import Dinari from '@dinari/api-sdk';
const client = new Dinari({
apiKeyID: process.env['DINARI_API_KEY_ID'], // This is the default and can be omitted
apiSecretKey: process.env['DINARI_API_SECRET_KEY'], // This is the default and can be omitted
environment: 'sandbox', // defaults to 'production'
});
async function main() {
const accountID = 'your-account-id';
const stockID = 'your-stock-id';
// Limit Buy Order
const limitBuy = await client.v2.accounts.orderRequests.createLimitBuy(accountID, {
asset_quantity: 1,
limit_price: 1.5,
stock_id: stockID,
});
console.log('Limit Buy Response:', limitBuy);
// Limit Sell Order
const limitSell = await client.v2.accounts.orderRequests.createLimitSell(accountID, {
asset_quantity: 1,
limit_price: 1.5,
stock_id: stockID,
});
console.log('Limit Sell Response:', limitSell);
// Market Buy Order
const marketBuy = await client.v2.accounts.orderRequests.createMarketBuy(accountID, {
stock_id: stockID,
payment_amount: 150.0,
});
console.log('Market Buy Response:', marketBuy);
// Market Sell Order
const marketSell = await client.v2.accounts.orderRequests.createMarketSell(accountID, {
stock_id: stockID,
asset_quantity: 1,
});
console.log('Market Sell Response:', marketSell);
}
main();
import os
from dinari_api_sdk import Dinari
client = Dinari(
api_key_id=os.environ.get("DINARI_API_KEY_ID"), # This is the default and can be omitted
api_secret_key=os.environ.get("DINARI_API_SECRET_KEY"), # This is the default and can be omitted
environment="sandbox", # defaults to "production"
)
account_id = "your-account-id"
stock_id = "your-stock-id"
# Limit Buy Order
limit_buy = client.v2.accounts.order_requests.create_limit_buy(
account_id=account_id,
asset_quantity=1,
limit_price=1.5,
stock_id=stock_id,
)
# Limit Sell Order
limit_sell = client.v2.accounts.order_requests.create_limit_sell(
account_id=account_id,
asset_quantity=1,
limit_price=1.5,
stock_id=stock_id,
)
# Market Buy Order
market_buy = client.v2.accounts.order_requests.create_market_buy(
account_id=account_id,
stock_id=stock_id,
payment_amount=150.0,
)
# Market Sell Order
market_sell = client.v2.accounts.order_requests.create_market_sell(
account_id=account_id,
stock_id=stock_id,
asset_quantity=1,
)
package main
import (
"context"
"fmt"
"log"
dinari "github.com/dinaricrypto/dinari-api-sdk-go"
"github.com/dinaricrypto/dinari-api-sdk-go/option"
)
func main() {
// DINARI_API_KEY_ID and DINARI_API_SECRET_KEY are set as environment variables
client := dinari.NewClient(
option.WithEnvironmentSandbox(), // Defaults to production when omitted
)
accountID := "your-account-id"
stockID := "your-stock-id"
// Limit Buy Order
limitBuyResp, err := client.V2.Accounts.OrderRequests.NewLimitBuy(context.TODO(), accountID, dinari.V2AccountOrderRequestNewLimitBuyParams{
CreateLimitOrderInput: dinari.CreateLimitOrderInputParam{
AssetQuantity: 1,
LimitPrice: 1.5,
StockID: stockID,
},
})
if err != nil {
log.Fatalf("Failed to create limit buy order: %v", err)
}
fmt.Printf("Limit Buy Response: %+v\n", limitBuyResp)
// Limit Sell Order
limitSellResp, err := client.V2.Accounts.OrderRequests.NewLimitSell(context.TODO(), accountID, dinari.V2AccountOrderRequestNewLimitSellParams{
CreateLimitOrderInput: dinari.CreateLimitOrderInputParam{
AssetQuantity: 1,
LimitPrice: 1.5,
StockID: stockID,
},
})
if err != nil {
log.Fatalf("Failed to create limit sell order: %v", err)
}
fmt.Printf("Limit Sell Response: %+v\n", limitSellResp)
// Market Buy Order
marketBuyResp, err := client.V2.Accounts.OrderRequests.NewMarketBuy(context.TODO(), accountID, dinari.V2AccountOrderRequestNewMarketBuyParams{
PaymentAmount: 150.0, // This is the dollar amount to spend
StockID: stockID,
})
if err != nil {
log.Fatalf("Failed to create market buy order: %v", err)
}
fmt.Printf("Market Buy Response: %+v\n", marketBuyResp)
// Market Sell Order
marketSellResp, err := client.V2.Accounts.OrderRequests.NewMarketSell(context.TODO(), accountID, dinari.V2AccountOrderRequestNewMarketSellParams{
AssetQuantity: 1,
StockID: stockID,
})
if err != nil {
log.Fatalf("Failed to create market sell order: %v", err)
}
fmt.Printf("Market Sell Response: %+v\n", marketSellResp)
}
Ordering using a Smart Contract
EVM
Standard Transaction
import Dinari from '@dinari/api-sdk';
import { resolveViemChain, sendOrderForViem } from '@dinari/viem-client';
import { createWalletClient, createPublicClient, custom, http, WalletClient } from 'viem';
const client = new Dinari({
apiKeyID: process.env['DINARI_API_KEY_ID'], // This is the default and can be omitted
apiSecretKey: process.env['DINARI_API_SECRET_KEY'], // This is the default and can be omitted
environment: 'sandbox', // defaults to 'production'
});
async function main() {
// Step 1. Prepare the Order
const accountId = 'your-account-id';
const caip2ChainId = 'eip155:421614';
const preparedOrder = await sClient.v2.accounts.orders.stocks.eip155.prepareOrder(accountId, {
chain_id: caip2ChainId,
order_side: 'BUY',
order_tif: 'DAY',
order_type: 'MARKET',
stock_id: '0196d545-d8a8-7210-8cd1-a49e82c31e53',
payment_token: "0x6a34FDFE60D1758dF5b577d413E37397D21c3E78",
payment_token_quantity: 10,
});
// Step 2: Sign and send the transaction data using @dinari/viem-client (In Development)
// If done in front end environment user will be prompted to sign each transaction.
const viemChain = resolveViemChain(caip2ChainId);
const walletClient = createWalletClient({ transport: custom((window as any).ethereum), chain: viemChain });
const txHashes = await sendOrderForViem(walletClient, caip2ChainId, preparedOrder);
}
main();
import { Eip155PrepareProxiedOrderResponse } from '@dinari/api-sdk/resources/v2/accounts/order-requests/stocks/eip155';
import { Eip155PrepareOrderResponse } from '@dinari/api-sdk/resources/v2/accounts/orders/stocks/eip155';
import { createPublicClient, http, WalletClient, PublicClient, TypedData, Address, Account } from 'viem';
import * as allChains from 'viem/chains';
/**
* Resolves a CAIP-2 formatted chain ID (e.g., "eip155:421614") or a plain numeric string (e.g., "1")
* to the corresponding viem chain object.
*/
export function resolveViemChain(chain_id: string): allChains.Chain {
// eg. Accepts "eip155:421614" or just "421614"
const match = chain_id.match(/^eip155:(\d+)$/);
const numericChainId = match ? Number(match[1]) : Number(chain_id);
const chain = Object.values(allChains).find((c: allChains.Chain) => c.id === numericChainId);
if (!chain) {
throw new Error(`Chain with id ${numericChainId} not found in viem/chains`);
}
return chain;
}
/**
* Sends all transactions in the given orderResponse sequentially using the provided viem WalletClient.
* Waits for each transaction to be mined before sending the next.
* Can be used in both frontend and backend environments.
*
* Backend requires that account is passed in
*/
export async function sendOrderForViem(
walletClient: WalletClient,
chain_id: string,
orderResponse: Eip155PrepareOrderResponse,
account?: Account,
publicClient?: PublicClient,
): Promise<{
txHashes: `0x${string}`[];
}> {
const chain = resolveViemChain(chain_id);
let resolvedPublicClient = publicClient;
if (!resolvedPublicClient) {
resolvedPublicClient = createPublicClient({ transport: http(), chain });
}
const txDatas = orderResponse.transaction_data;
if (!Array.isArray(txDatas) || txDatas.length === 0) {
throw new Error('transaction_data is missing or empty');
}
let resolvedAccount: Address | Account;
if (account) {
resolvedAccount = account;
} else {
[resolvedAccount] = await walletClient.requestAddresses();
}
const txHashes: `0x${string}`[] = [];
for (const txData of txDatas) {
if (!txData || !txData.contract_address || !txData.data) {
throw new Error('transaction_data item is missing required fields');
}
// 1. Sign and send transaction
const txHash = await walletClient.sendTransaction({
to: txData.contract_address as `0x${string}`,
data: txData.data as `0x${string}`,
account: resolvedAccount,
chain,
});
// 2. Wait for the transaction to be mined before proceeding to the next
await resolvedPublicClient.waitForTransactionReceipt({ hash: txHash });
txHashes.push(txHash);
}
return { txHashes };
}
/**
* Prompts the user or backend signer to sign the permit and order typed data using the provided viem WalletClient.
* Returns the signatures for both permit and order.
*
* Backend requires that account is passed in
*/
export async function signTransferPermitAndOrderForViem(
walletClient: WalletClient,
orderResponse: Eip155PrepareProxiedOrderResponse,
account?: Account,
): Promise<{
permitSignature: `0x${string}`;
orderSignature: `0x${string}`;
}> {
const { permit_typed_data, order_typed_data } = orderResponse;
if (!permit_typed_data.domain || !permit_typed_data.types || !permit_typed_data.message) {
throw new Error('permit_typed_data is missing required fields');
}
if (!order_typed_data.domain || !order_typed_data.types || !order_typed_data.message) {
throw new Error('order_typed_data is missing required fields');
}
let resolvedAccount: Address | Account;
if (account) {
resolvedAccount = account;
} else {
[resolvedAccount] = await walletClient.requestAddresses();
}
const permitSignature = await walletClient.signTypedData({
domain: permit_typed_data.domain,
types: permit_typed_data.types as TypedData,
primaryType: permit_typed_data.primaryType,
message: permit_typed_data.message as Record<string, unknown>,
account: resolvedAccount,
});
const orderSignature = await walletClient.signTypedData({
domain: order_typed_data.domain,
types: order_typed_data.types as TypedData,
primaryType: order_typed_data.primaryType,
message: order_typed_data.message as Record<string, unknown>,
account: resolvedAccount,
});
return {
permitSignature,
orderSignature,
};
}
import os
from dinari_api_sdk import Dinari
from eth_account import Account
from eth_account.messages import encode_typed_data
client = Dinari(
api_key_id=os.environ.get("DINARI_API_KEY_ID"), # This is the default and can be omitted
api_secret_key=os.environ.get("DINARI_API_SECRET_KEY"), # This is the default and can be omitted
environment="sandbox", # defaults to "production"
)
account_id = "your-account-id"
private_key = "your-wallet-private-key"
acct: LocalAccount = Account.from_key(private_key)
# Step 1. Prepare Order
order = client.v2.accounts.orders.stocks.eip155.prepare_order(
account_id=account_id,
chain_id="eip155:421614",
order_side="BUY",
order_tif="DAY",
order_type="MARKET",
stock_id="0196d545-d8a8-7210-8cd1-a49e82c31e53",
payment_token="0x6a34FDFE60D1758dF5b577d413E37397D21c3E78",
payment_token_quantity=1.0,
)
# Step 2. Sign and send transaction_data in sequential order
txHashes = []
for data in order.transaction_data:
tx = {
"to": data.contract_address,
"data": data.data,
"value": 0,
"gas": w3.eth.estimate_gas(
{
"from": acct.address,
"to": data.contract_address,
"data": data.data,
}
),
"gasPrice": w3.eth.gas_price,
"nonce": w3.eth.get_transaction_count(acct.address),
"chainId": 421614, # e.g. Arbitrum Sepolia
}
signed = acct.sign_transaction(tx)
txHash = w3.eth.send_raw_transaction(signed.rawTransaction)
txHashes.append(txHash)
receipt = w3.eth.wait_for_transaction_receipt(txHash)
if receipt.status != 1:
break
print(txHashes)
package main
import (
"context"
"fmt"
"log"
"github.com/chenzhijie/go-web3"
dinari "github.com/dinaricrypto/dinari-api-sdk-go"
"github.com/dinaricrypto/dinari-api-sdk-go/option"
)
func main() {
// DINARI_API_KEY_ID and DINARI_API_SECRET_KEY are set as environment variables
client := dinari.NewClient(
option.WithEnvironmentSandbox(), // Defaults to production when omitted
)
accountID := "your-account-id"
// Step 1. Prepare Order
prepareOrderBody := dinari.V2AccountOrderStockEip155PrepareOrderParams{
ChainID: dinari.ChainEip155_421614,
OrderSide: dinari.OrderSideBuy,
OrderTif: dinari.OrderTifDay,
OrderType: dinari.OrderTypeMarket,
StockID: "0196d545-d8a8-7210-8cd1-a49e82c31e53",
PaymentToken: dinari.String("0x6a34FDFE60D1758dF5b577d413E37397D21c3E78"),
PaymentTokenQuantity: dinari.Float(1.0),
}
preparedOrderResp, err := client.V2.Accounts.Orders.Stocks.Eip155.PrepareOrder(context.TODO(), accountID, prepareOrderBody)
if err != nil {
log.Fatalf("Failed: %v", err)
}
var rpcProviderURL = "https://sepolia-rollup.arbitrum.io/rpc"
w3, err := web3.NewWeb3(rpcProviderURL)
if err != nil {
panic(err)
}
w3.Eth.SetChainId(421614)
privateKey := "your-wallet-private-key"
err = w3.Eth.SetAccount(privateKey)
if err != nil {
panic(err)
}
// Step 2. Send Order
txHashes, err := SendOrder(context.TODO(), preparedOrderResp, w3)
if err != nil {
panic(err)
}
fmt.Printf("hashes: %+v\n", txHashes)
}
package main
import (
"context"
"fmt"
"math/big"
"github.com/chenzhijie/go-web3"
"github.com/chenzhijie/go-web3/types"
dinari "github.com/dinaricrypto/dinari-api-sdk-go"
"github.com/ethereum/go-ethereum/common"
)
func SendOrder(
ctx context.Context,
orderResp *dinari.V2AccountOrderStockEip155PrepareOrderResponse,
w3 *web3.Web3,
) ([]string, error) {
if len(orderResp.TransactionData) == 0 {
return nil, fmt.Errorf("transaction_data is missing or empty")
}
walletAddress := w3.Eth.Address()
txHashes := []string{}
for i, tx := range orderResp.TransactionData {
if tx.ContractAddress == "" || tx.Data == "" {
return nil, fmt.Errorf("txData[%d] is missing required fields", i)
}
// Get nonce and gas price
nonce, err := w3.Eth.GetNonce(walletAddress, nil)
if err != nil {
return nil, fmt.Errorf("get nonce failed: %w", err)
}
gasPrice, err := w3.Eth.GasPrice()
if err != nil {
return nil, fmt.Errorf("get gas price failed: %w", err)
}
// Construct transaction data
to := common.HexToAddress(tx.ContractAddress)
amount := big.NewInt(0) // no ETH transfer
gasLimit, err := w3.Eth.EstimateGas(&types.CallMsg{
From: walletAddress,
To: to,
Data: common.FromHex(tx.Data), // ABI-encoded method call
Value: types.NewCallMsgBigInt(big.NewInt(0)),
})
if err != nil {
return nil, fmt.Errorf("estimate gas failed: %w", err)
}
data := tx.Data
gasPriceInt := new(big.Int).SetUint64(gasPrice)
receipt, err := w3.Eth.SyncSendRawTransaction(to, amount, nonce, gasLimit, gasPriceInt, common.FromHex(data))
if err != nil {
return nil, fmt.Errorf("send tx failed: %w", err)
}
txHashes = append(txHashes, receipt.TxHash.Hex())
}
return txHashes, nil
}
Proxied Transaction aka Gas-sponsored
import Dinari from '@dinari/api-sdk';
import { resolveViemChain, signTransferPermitAndOrderForViem } from '@dinari/viem-client';
import { createWalletClient, custom } from 'viem';
const client = new Dinari({
apiKeyID: process.env['DINARI_API_KEY_ID'], // This is the default and can be omitted
apiSecretKey: process.env['DINARI_API_SECRET_KEY'], // This is the default and can be omitted
environment: 'sandbox', // defaults to 'production'
});
async function main() {
// Step 1. Prepare the Proxied Order
const accountId = 'your-account-id';
const caip2ChainId = 'eip155:421614';
const preparedProxiedOrder = await client.v2.accounts.orderRequests.stocks.eip155.prepareProxiedOrder(accountId, {
chain_id: caip2ChainId,
order_side: 'BUY',
order_tif: 'DAY',
order_type: 'MARKET',
stock_id: '0196d545-d8a8-7210-8cd1-a49e82c31e53',
payment_token: "0x6a34FDFE60D1758dF5b577d413E37397D21c3E78",
payment_token_quantity: 10,
});
// Step 2. The user signs the permit and order transactions off-chain
// Example using viem and @dinari/viem-client (In Development)
// Step 2 can be done in either backend or frontend environments
// The following assumes it's run in frontend
// If done in front end environment user will be prompted to sign each transaction.
const viemChain = resolveViemChain(caip2ChainId);
const walletClient = createWalletClient({ transport: custom((window as any).ethereum), chain: viemChain });
const { permitSignature, orderSignature } = await signTransferPermitAndOrderForViem(walletClient, preparedProxiedOrder);
// Step 3. The permit and order signatures are used to submit the Proxied Order
const createdProxiedOrder = await client.v2.accounts.orderRequests.stocks.eip155.createProxiedOrder(accountId,
{
order_signature: orderSignature,
permit_signature: permitSignature,
prepared_proxied_order_id: preparedProxiedOrder.id,
}
);
}
main();
import { Eip155PrepareProxiedOrderResponse } from '@dinari/api-sdk/resources/v2/accounts/order-requests/stocks/eip155';
import { Eip155PrepareOrderResponse } from '@dinari/api-sdk/resources/v2/accounts/orders/stocks/eip155';
import { createPublicClient, http, WalletClient, PublicClient, TypedData, Address, Account } from 'viem';
import * as allChains from 'viem/chains';
/**
* Resolves a CAIP-2 formatted chain ID (e.g., "eip155:421614") or a plain numeric string (e.g., "1")
* to the corresponding viem chain object.
*/
export function resolveViemChain(chain_id: string): allChains.Chain {
// eg. Accepts "eip155:421614" or just "421614"
const match = chain_id.match(/^eip155:(\d+)$/);
const numericChainId = match ? Number(match[1]) : Number(chain_id);
const chain = Object.values(allChains).find((c: allChains.Chain) => c.id === numericChainId);
if (!chain) {
throw new Error(`Chain with id ${numericChainId} not found in viem/chains`);
}
return chain;
}
/**
* Sends all transactions in the given orderResponse sequentially using the provided viem WalletClient.
* Waits for each transaction to be mined before sending the next.
* Can be used in both frontend and backend environments.
*
* Backend requires that account is passed in
*/
export async function sendOrderForViem(
walletClient: WalletClient,
chain_id: string,
orderResponse: Eip155PrepareOrderResponse,
account?: Account,
publicClient?: PublicClient,
): Promise<{
txHashes: `0x${string}`[];
}> {
const chain = resolveViemChain(chain_id);
let resolvedPublicClient = publicClient;
if (!resolvedPublicClient) {
resolvedPublicClient = createPublicClient({ transport: http(), chain });
}
const txDatas = orderResponse.transaction_data;
if (!Array.isArray(txDatas) || txDatas.length === 0) {
throw new Error('transaction_data is missing or empty');
}
let resolvedAccount: Address | Account;
if (account) {
resolvedAccount = account;
} else {
[resolvedAccount] = await walletClient.requestAddresses();
}
const txHashes: `0x${string}`[] = [];
for (const txData of txDatas) {
if (!txData || !txData.contract_address || !txData.data) {
throw new Error('transaction_data item is missing required fields');
}
// 1. Sign and send transaction
const txHash = await walletClient.sendTransaction({
to: txData.contract_address as `0x${string}`,
data: txData.data as `0x${string}`,
account: resolvedAccount,
chain,
});
// 2. Wait for the transaction to be mined before proceeding to the next
await resolvedPublicClient.waitForTransactionReceipt({ hash: txHash });
txHashes.push(txHash);
}
return { txHashes };
}
/**
* Prompts the user or backend signer to sign the permit and order typed data using the provided viem WalletClient.
* Returns the signatures for both permit and order.
*
* Backend requires that account is passed in
*/
export async function signTransferPermitAndOrderForViem(
walletClient: WalletClient,
orderResponse: Eip155PrepareProxiedOrderResponse,
account?: Account,
): Promise<{
permitSignature: `0x${string}`;
orderSignature: `0x${string}`;
}> {
const { permit_typed_data, order_typed_data } = orderResponse;
if (!permit_typed_data.domain || !permit_typed_data.types || !permit_typed_data.message) {
throw new Error('permit_typed_data is missing required fields');
}
if (!order_typed_data.domain || !order_typed_data.types || !order_typed_data.message) {
throw new Error('order_typed_data is missing required fields');
}
let resolvedAccount: Address | Account;
if (account) {
resolvedAccount = account;
} else {
[resolvedAccount] = await walletClient.requestAddresses();
}
const permitSignature = await walletClient.signTypedData({
domain: permit_typed_data.domain,
types: permit_typed_data.types as TypedData,
primaryType: permit_typed_data.primaryType,
message: permit_typed_data.message as Record<string, unknown>,
account: resolvedAccount,
});
const orderSignature = await walletClient.signTypedData({
domain: order_typed_data.domain,
types: order_typed_data.types as TypedData,
primaryType: order_typed_data.primaryType,
message: order_typed_data.message as Record<string, unknown>,
account: resolvedAccount,
});
return {
permitSignature,
orderSignature,
};
}
import os
from dinari_api_sdk import Dinari
from eth_account import Account
from eth_account.messages import encode_typed_data
client = Dinari(
api_key_id=os.environ.get("DINARI_API_KEY_ID"), # This is the default and can be omitted
api_secret_key=os.environ.get("DINARI_API_SECRET_KEY"), # This is the default and can be omitted
environment="sandbox", # defaults to "production"
)
account_id = "your-account-id"
# Step 1. Prepare Proxied Order
proxied_order = client.v2.accounts.order_requests.stocks.eip155.prepare_proxied_order(
account_id=account_id,
chain_id="eip155:421614",
order_side="BUY",
order_tif="DAY",
order_type="MARKET",
stock_id="0196d545-d8a8-7210-8cd1-a49e82c31e53",
payment_token="0x6a34FDFE60D1758dF5b577d413E37397D21c3E78",
payment_token_quantity=1.0,
)
private_key = "your-wallet-private-key"
acct = Account.from_key(private_key)
# Step 2. Sign Order and permit typed data
typed_data = {
"types": proxied_order.permit_typed_data.types,
"domain": proxied_order.permit_typed_data.domain,
"primaryType": proxied_order.permit_typed_data.primary_type,
"message": proxied_order.permit_typed_data.message,
}
message = encode_typed_data(full_message=typed_data)
permit_signature = acct.sign_message(message)
typed_data = {
"types": proxied_order.order_typed_data.types,
"domain": proxied_order.order_typed_data.domain,
"primaryType": proxied_order.order_typed_data.primary_type,
"message": proxied_order.order_typed_data.message,
}
message = encode_typed_data(full_message=typed_data)
order_signature = acct.sign_message(message)
# Step 3. Created Proxied Order
order_request = client.v2.accounts.order_requests.stocks.eip155.create_proxied_order(
account_id=account_id,
order_signature=order_signature.signature.hex(),
permit_signature=permit_signature.signature.hex(),
prepared_proxied_order_id=proxied_order.id,
)
print(order_request)
package main
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"log"
"github.com/chenzhijie/go-web3"
dinari "github.com/dinaricrypto/dinari-api-sdk-go"
"github.com/dinaricrypto/dinari-api-sdk-go/option"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
)
func main() {
// DINARI_API_KEY_ID and DINARI_API_SECRET_KEY are set as environment variables
client := dinari.NewClient(
option.WithEnvironmentSandbox(), // Defaults to production when omitted
)
accountID := "your-account-id"
// Step 1. Prepare the Proxied Order
proxiedOrderParams := dinari.V2AccountOrderRequestStockEip155PrepareProxiedOrderParams{
ChainID: dinari.ChainEip155_421614,
OrderSide: dinari.OrderSideBuy,
OrderTif: dinari.OrderTifDay,
OrderType: dinari.OrderTypeMarket,
StockID: "0196d545-d8a8-7210-8cd1-a49e82c31e53",
PaymentToken: dinari.String("0x6a34FDFE60D1758dF5b577d413E37397D21c3E78"),
PaymentTokenQuantity: dinari.Float(1.0),
}
proxiedOrder, err :=
client.V2.Accounts.OrderRequests.Stocks.Eip155.PrepareProxiedOrder(context.TODO(), accountID, proxiedOrderParams)
if err != nil {
log.Fatalf("Failed: %v", err)
}
// Step 2. Sign Permit and Order Data
var rpcProviderURL = "https://sepolia-rollup.arbitrum.io/rpc"
w3, err := web3.NewWeb3(rpcProviderURL)
if err != nil {
panic(err)
}
privateKeyHex := "your-wallet-private-key"
err = w3.Eth.SetAccount(privateKeyHex)
if err != nil {
panic(err)
}
permitSig, err := signTypedDataPayload(w3, DinariTypedData{
Domain: proxiedOrder.PermitTypedData.Domain,
Message: proxiedOrder.PermitTypedData.Message,
PrimaryType: proxiedOrder.PermitTypedData.PrimaryType,
Types: proxiedOrder.PermitTypedData.Types,
})
if err != nil {
panic(err)
}
orderSig, err := signTypedDataPayload(w3, DinariTypedData{
Domain: proxiedOrder.OrderTypedData.Domain,
Message: proxiedOrder.OrderTypedData.Message,
PrimaryType: proxiedOrder.OrderTypedData.PrimaryType,
Types: proxiedOrder.OrderTypedData.Types,
})
if err != nil {
panic(err)
}
// Step 3. Create Proxied Order with Permit and Order Data Signatures
createBody := dinari.V2AccountOrderRequestStockEip155NewProxiedOrderParams{
OrderSignature: "0x" + hex.EncodeToString(orderSig),
PermitSignature: "0x" + hex.EncodeToString(permitSig),
PreparedProxiedOrderID: proxiedOrder.ID,
}
submittedProxiedOrder, err :=
client.V2.Accounts.OrderRequests.Stocks.Eip155.NewProxiedOrder(context.TODO(), accountID, createBody)
if err != nil {
log.Fatalf("Failed: %v", err)
}
fmt.Printf("Submitted Order: %+v\n", submittedProxiedOrder)
}
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/chenzhijie/go-web3"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
)
type DinariTypedData struct {
Domain any `json:"domain,required"`
Message any `json:"message,required"`
PrimaryType string `json:"primaryType,required"`
Types any `json:"types,required"`
}
func signTypedDataPayload(w3 *web3.Web3, rawTypedData DinariTypedData) ([]byte, error) {
var domain apitypes.TypedDataDomain
domainRaw := rawTypedData.Domain
domainJSON, err := json.Marshal(domainRaw)
if err != nil {
log.Fatalf("failed to marshal domain: %v", err)
}
if err := json.Unmarshal(domainJSON, &domain); err != nil {
log.Fatalf("failed to unmarshal domain: %v", err)
}
var types apitypes.Types
typesraw := rawTypedData.Types
typesJSON, err := json.Marshal(typesraw)
if err != nil {
log.Fatalf("failed to marshal types: %v", err)
}
if err := json.Unmarshal(typesJSON, &types); err != nil {
log.Fatalf("failed to unmarshal types: %v", err)
}
json.Unmarshal(typesJSON, &types)
msg, ok := rawTypedData.Message.(apitypes.TypedDataMessage)
if !ok {
return nil, fmt.Errorf("message is not apitypes.TypedDataMessage")
}
sig, err := w3.Eth.SignTypedData(apitypes.TypedData{
Types: types,
PrimaryType: rawTypedData.PrimaryType,
Domain: domain,
Message: msg,
})
if err != nil {
return nil, fmt.Errorf("signing error: %w", err)
}
return sig, nil
}
Updated 12 days ago