Placing dShare Orders

Before an Entity can place a dShare order, make sure it fulfills the following pre-requisites:

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
}