DFXswiss / api

DFX API
https://api.dfx.swiss
MIT License
12 stars 31 forks source link

Solana Integration #765

Closed davidleomay closed 10 months ago

davidleomay commented 1 year ago

Required functionalities

Details

Signatures DFX is required by law to verify that the address of a user really belongs to the user. This is done with a digital signature. The user signs a message with his private key and uses his address and the resulting signature to log in at DFX.

Swaps It might be interesting to do swaps on a decentralized exchange (like Uniswap on Ethereum), if something like this exists on Solana. In addition we would need to do test swap for price estimations.

Input Registration Our users should be able to send funds from every address (or even central exchanges) to DFX's off-ramp. We therefore assign every user a unique deposit address. As soon as some funds arrive there, we sell them and pay the fiat out to the user's bank account. We need an efficient system to generate many addresses and register incoming transactions.

dannpl commented 1 year ago

Awesome!!!

dannpl commented 1 year ago

Transaction sign with Keypair

import {
  createTransferCheckedInstruction,
  getAssociatedTokenAddress,
  getOrCreateAssociatedTokenAccount,
} from "@solana/spl-token";
import { Keypair } from "@solana/web3.js";

const mint = new PublicKey(USDC_MINT_ADDRESS);
const payer = new Keypair()
let instructions: TransactionInstruction[] = [];

const payerATA = await getOrCreateAssociatedTokenAccount(
  connection,
  payer,
  mint,
  payer.publicKey
);

const tesouryAcc = await getAssociatedTokenAddress(
  mint,
  new PublicKey(TESOURY_ACCOUNT)
);

instructionsUsdc.push(
  createTransferCheckedInstruction(
    payerATA.address,
    mint,
    tesouryAcc,
    payer.publicKey,
    payerATA.amount,
    6,
    [payer]
  )
);

const {
  blockhash,
  lastValidBlockHeight,
} = await connection.getLatestBlockhash();

const messageV0 = new TransactionMessage({
  payerKey: payer.publicKey,
  recentBlockhash: blockhash,
  instructions: instructions,
}).compileToV0Message();

const transaction = new VersionedTransaction(messageV0);

transaction.sign([payer]);

const signature = await connection.sendTransaction(transaction, {
  maxRetries: 5,
});

const confirmTransaction = await connection.confirmTransaction(
  {
    signature: signature,
    blockhash,
    lastValidBlockHeight,
  },
  "confirmed"
);

if (confirmTransaction.value.err !== null) return;
dannpl commented 1 year ago
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
import {
    PublicKey,
    SystemProgram,
    SYSVAR_RENT_PUBKEY,
    TransactionInstruction
} from "@solana/web3.js";

export const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = new PublicKey(
  "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
);

export function createAssociatedTokenAccountInstruction(
  associatedTokenAddress: PublicKey,
  payer: PublicKey,
  walletAddress: PublicKey,
  splTokenMintAddress: PublicKey
) {
  const keys = [
    {
      pubkey: payer,
      isSigner: true,
      isWritable: true,
    },
    {
      pubkey: associatedTokenAddress,
      isSigner: false,
      isWritable: true,
    },
    {
      pubkey: walletAddress,
      isSigner: false,
      isWritable: false,
    },
    {
      pubkey: splTokenMintAddress,
      isSigner: false,
      isWritable: false,
    },
    {
      pubkey: SystemProgram.programId,
      isSigner: false,
      isWritable: false,
    },
    {
      pubkey: TOKEN_PROGRAM_ID,
      isSigner: false,
      isWritable: false,
    },
    {
      pubkey: SYSVAR_RENT_PUBKEY,
      isSigner: false,
      isWritable: false,
    },
  ];
  return new TransactionInstruction({
    keys,
    programId: SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
    data: Buffer.from([]),
  });
}

Transaction sign with wallet adapter
```ts
import {
  createTransferCheckedInstruction,
  getAccount,
  getAssociatedTokenAddress
} from "@solana/spl-token";
import { createAssociatedTokenAccountInstruction } from "utils/transaction-instruction";

const payerPublicKey = userWallet.publicKey;
const mintPublicKey = new PublicKey(USDC_MINT_ADDRESS);
const recipientPubKey = new PublicKey(recipient.walletAddress);

const ownerATA = await getAssociatedTokenAddress(
  mintPublicKey,
  payerPublicKey
);

const instructions: TransactionInstruction[] = [];

if (!(await connection.getAccountInfo(ownerATA))) {
  instructions.push(
    createAssociatedTokenAccountInstruction(
      ownerATA,
      payerPublicKey,
      payerPublicKey,
      mintPublicKey
    )
  );
}

const recipientATA = await getAssociatedTokenAddress(
  mintPublicKey,
  recipientPubKey
);

if (!(await connection.getAccountInfo(recipientATA))) {
  instructions.push(
    createAssociatedTokenAccountInstruction(
      recipientATA,
      payerPublicKey,
      recipientPubKey,
      mintPublicKey
    )
  );
}

const totalValue = 100

instructions.push(
  createTransferCheckedInstruction(
    ownerATA, // from (should be a token account)
    mintPublicKey, // mint
    recipientATA, // to (should be a token account)
    payerPublicKey, // from's owner
    new BN(totalValue * 1000000), // 1000_000 6 decimals place need get custom for each SPL token
    6
  )
);

const { blockhash } = await connection.getLatestBlockhash();

const solanaTransaction = new Transaction().add(...instructions);

solanaTransaction.recentBlockhash = blockhash;
solanaTransaction.feePayer = payerPublicKey;
solanaTransaction.instructions = instructions;

const transaction = await userWallet.signTransaction(solanaTransaction);
const newTransaction = transaction.serialize();

const signature = await connection.sendRawTransaction(newTransaction);
dannpl commented 1 year ago

Sign Message

Sign Message with wallet adapter

const VERIFY_USER = `DFXswiss`
const signature = await wallet.signMessage(VERIFY_USER)
const serializedSignature = bs58.encode(signature);

Verify Message

const VERIFY_USER = `DFXswiss`
const verifyMessage = sign.detached.verify(
  new TextEncoder().encode(VERIFY_USER),
  bs58.decode(sigDecrypted),
  new PublicKey(user.walletAddress).toBytes()
);
dannpl commented 1 year ago

Wallet Connet

import {
  ConnectionProvider,
  WalletProvider
} from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import { PhantomWalletAdapter } from "@solana/wallet-adapter-wallets";

const wallets = useMemo(() => [new PhantomWalletAdapter()], []); // You can put some here like Backpack, etc..

const connection = useMemo(() => new Connection(HELIUS_API), []);

<ConnectionProvider endpoint={RPC}>
  <WalletProvider wallets={wallets} autoConnect>
    <WalletModalProvider>{children}</WalletModalProvider>
  </WalletProvider>
</ConnectionProvider>

import { useWallet } from "@solana/wallet-adapter-react";
const wallet = useWallet();

wallet.connect(); // Show Modal with the wallets
TaprootFreak commented 10 months ago

Hi @dannpl Thank you very much for your support!

We have decided to integrate Solana as soon as we have a large Soalana wallet that integrates us.

If you can help us with this, please reply to this message. In the meantime, I will close this issue.