Skip to content

Commit

Permalink
feat: encode reference type and id into the Kin memo field
Browse files Browse the repository at this point in the history
  • Loading branch information
beeman committed Jan 23, 2023
1 parent 85baca0 commit 1a4f251
Show file tree
Hide file tree
Showing 18 changed files with 167 additions and 30 deletions.
2 changes: 2 additions & 0 deletions libs/api/kinetic/data-access/src/lib/api-kinetic.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ export class ApiKineticService implements OnModuleInit {
blockhash,
index: input.index,
lastValidBlockHeight,
referenceId: input.referenceId,
referenceType: input.referenceType,
signer: signer.solana,
tokenAccount: tokenAccount.account,
})
Expand Down
22 changes: 16 additions & 6 deletions libs/sdk/src/lib/kinetic-sdk-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,17 @@ export class KineticSdkInternal {
const appConfig = this.ensureAppConfig()
const appMint = getAppMint(appConfig, options.mint?.toString())
const commitment = this.getCommitment(options.commitment)
const referenceId = options.referenceId || null
const referenceType = options.referenceType || null

const request: CloseAccountRequest = {
account: options.account.toString(),
commitment,
environment: this.sdkConfig.environment,
index: this.sdkConfig.index,
mint: appMint.publicKey,
referenceId: options.referenceId,
referenceType: options.referenceType,
referenceId,
referenceType,
}

return this.accountApi
Expand All @@ -90,6 +92,8 @@ export class KineticSdkInternal {
const appConfig = this.ensureAppConfig()
const appMint = getAppMint(appConfig, options.mint?.toString())
const commitment = this.getCommitment(options.commitment)
const referenceId = options.referenceId || null
const referenceType = options.referenceType || null

const existing = await this.findTokenAccount({
account: options.owner.publicKey,
Expand All @@ -115,6 +119,8 @@ export class KineticSdkInternal {
mintPublicKey: appMint.publicKey,
owner: options.owner.solana,
ownerTokenAccount,
referenceId,
referenceType,
})

const request: CreateAccountRequest = {
Expand All @@ -123,8 +129,8 @@ export class KineticSdkInternal {
index: this.sdkConfig.index,
lastValidBlockHeight,
mint: appMint.publicKey,
referenceId: options.referenceId,
referenceType: options.referenceType,
referenceId,
referenceType,
tx: serializeTransaction(tx),
}

Expand Down Expand Up @@ -250,6 +256,8 @@ export class KineticSdkInternal {

const destination = options.destination.toString()
const senderCreate = options.senderCreate || false
const referenceId = options.referenceId || null
const referenceType = options.referenceType || null

// We get the token account for the owner
const ownerTokenAccount = await this.findTokenAccount({
Expand Down Expand Up @@ -301,6 +309,8 @@ export class KineticSdkInternal {
mintPublicKey: appMint.publicKey,
owner: options.owner.solana,
ownerTokenAccount,
referenceId,
referenceType,
senderCreate: senderCreate && !!senderCreateTokenAccount,
type: options.type || TransactionType.None,
})
Expand All @@ -311,8 +321,8 @@ export class KineticSdkInternal {
index: this.sdkConfig.index,
lastValidBlockHeight,
mint: appMint.publicKey,
referenceId: options.referenceId,
referenceType: options.referenceType,
referenceId,
referenceType,
tx: serializeTransaction(tx),
}).catch((err) => {
throw new Error(err?.response?.data?.message ?? 'Unknown error')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`createReferenceKey should create a reference key based on id 1`] = `"my-reference-id"`;

exports[`createReferenceKey should create a reference key based on type 1`] = `"my-type"`;

exports[`createReferenceKey should create a reference key based on type and id 1`] = `"my-type:my-reference-id"`;
39 changes: 39 additions & 0 deletions libs/solana/src/lib/helpers/create-reference-key.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createReferenceKey } from './create-reference-key'

describe('createReferenceKey', () => {
it('should create a reference key based on type and id', () => {
const key = createReferenceKey({ type: 'my-type', id: 'my-reference-id' })

expect(key).toMatchSnapshot()
})

it('should create a reference key based on type', () => {
const key = createReferenceKey({ type: 'my-type' })

expect(key).toMatchSnapshot()
})

it('should create a reference key based on id', () => {
const key = createReferenceKey({ id: 'my-reference-id' })

expect(key).toMatchSnapshot()
})

it('should return undefined if no type or id is provided', () => {
const key = createReferenceKey({})

expect(key).toBeUndefined()
})

it('should fail if a type is longer than 10 characters', () => {
expect(() => createReferenceKey({ type: 'my-type-is-longer-than-10-characters' })).toThrow(
'Reference type must be 10 characters or less',
)
})

it('should fail if an id is longer than 64 characters', () => {
expect(() => createReferenceKey({ id: 'abcdefghijklmnopqrstuvwxyz1111' })).toThrow(
'Reference id must be 26 characters or less',
)
})
})
31 changes: 31 additions & 0 deletions libs/solana/src/lib/helpers/create-reference-key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Create a reference key from a type and id.
* @param {string} type
* @param {string} id
* @returns {string}
*/
export function createReferenceKey({ type, id }: { type?: string | null; id?: string | null }): string | undefined {
if (!type && !id) {
return undefined
}

// Fail if type is longer than 10 characters
if (type && type.length > 10) {
throw new Error('Reference type must be 10 characters or less')
}
// Fail if id is longer than 26 characters
if (id && id.length > 26) {
throw new Error('Reference id must be 26 characters or less')
}

if (type && id) {
return `${type}:${id}`
}
if (type) {
return type
}
if (id) {
return id
}
return undefined
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import { createCloseAccountInstruction } from '@solana/spl-token'
import { Transaction, TransactionInstruction } from '@solana/web3.js'
import { GenerateCloseAccountTransactionOptions } from '../interfaces'
import { generateKinMemoInstruction, TransactionType } from '../kin'
import { createReferenceKey } from './create-reference-key'
import { getPublicKey } from './get-public-key'

export function generateCloseAccountTransaction({
addMemo,
blockhash,
index,
lastValidBlockHeight,
referenceId,
referenceType,
signer,
tokenAccount,
}: GenerateCloseAccountTransactionOptions): {
Expand All @@ -22,6 +25,7 @@ export function generateCloseAccountTransaction({
instructions.push(
generateKinMemoInstruction({
index,
reference: createReferenceKey({ type: referenceType, id: referenceId }),
type: TransactionType.None,
}),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
import { Transaction, TransactionInstruction } from '@solana/web3.js'
import { GenerateCreateAccountTransactionOptions } from '../interfaces'
import { TransactionType } from '../kin'
import { generateKinMemoInstruction } from '../kin/generate-kin-memo-instruction'
import { generateKinMemoInstruction } from '../kin'
import { createReferenceKey } from './create-reference-key'
import { getPublicKey } from './get-public-key'

export async function generateCreateAccountTransaction(
Expand All @@ -26,6 +27,7 @@ export async function generateCreateAccountTransaction(
instructions.push(
generateKinMemoInstruction({
index: options.index,
reference: createReferenceKey({ type: options.referenceType, id: options.referenceId }),
type: TransactionType.None,
}),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { createTransferCheckedInstruction, TOKEN_PROGRAM_ID } from '@solana/spl-token'
import { Transaction, TransactionInstruction } from '@solana/web3.js'
import { GenerateMakeTransferBatchTransactionsOptions } from '../interfaces'
import { generateKinMemoInstruction } from '../kin/generate-kin-memo-instruction'
import { generateKinMemoInstruction } from '../kin'
import { addDecimals } from './add-remove-decimals'
import { createReferenceKey } from './create-reference-key'
import { getPublicKey } from './get-public-key'

export async function generateMakeTransferBatchTransaction(
Expand All @@ -22,6 +23,7 @@ export async function generateMakeTransferBatchTransaction(
instructions.push(
generateKinMemoInstruction({
index: options.index,
reference: createReferenceKey({ type: options.referenceType, id: options.referenceId }),
type: options.type,
}),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {
} from '@solana/spl-token'
import { Transaction, TransactionInstruction } from '@solana/web3.js'
import { GenerateMakeTransferOptions } from '../interfaces'
import { generateKinMemoInstruction } from '../kin/generate-kin-memo-instruction'
import { generateKinMemoInstruction } from '../kin'
import { addDecimals } from './add-remove-decimals'
import { createReferenceKey } from './create-reference-key'
import { getPublicKey } from './get-public-key'

export function generateMakeTransferTransaction(options: GenerateMakeTransferOptions): Transaction {
Expand All @@ -26,6 +27,7 @@ export function generateMakeTransferTransaction(options: GenerateMakeTransferOpt
instructions.push(
generateKinMemoInstruction({
index: options.index,
reference: createReferenceKey({ type: options.referenceType, id: options.referenceId }),
type: options.type,
}),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export interface GenerateCloseAccountTransactionOptions {
blockhash: string
index: number
lastValidBlockHeight: number
tokenAccount: PublicKeyString
referenceId?: string
referenceType?: string
signer: Keypair
tokenAccount: PublicKeyString
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ export interface GenerateCreateAccountTransactionOptions {
mintPublicKey: PublicKeyString
owner: Keypair
ownerTokenAccount: PublicKeyString
referenceId?: string | null
referenceType?: string | null
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ export interface GenerateMakeTransferBatchTransactionsOptions {
mintPublicKey: PublicKeyString
owner: Keypair
ownerTokenAccount: PublicKeyString
referenceId?: string | null
referenceType?: string | null
type: TransactionType
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface GenerateMakeTransferOptions {
mintPublicKey: PublicKeyString
owner: Keypair
ownerTokenAccount: PublicKeyString
referenceId?: string | null
referenceType?: string | null
senderCreate?: boolean
type: TransactionType
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ Object {
81,
81,
65,
65,
65,
65,
65,
65,
65,
65,
65,
65,
65,
65,
65,
65,
65,
65,
116,
74,
55,
55,
51,
54,
112,
52,
116,
90,
55,
55,
74,
119,
73,
65,
65,
65,
Expand Down
25 changes: 25 additions & 0 deletions libs/solana/src/lib/kin/create-kin-memo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,29 @@ describe('createKinMemo', () => {
expect(parsed.transactionType()).toEqual(TransactionType.P2P)
expect(parsed.index()).toEqual(1)
})

it('create memo with reference data', () => {
const referenceType = '1234567890'
const referenceId = 'abcdefghijklmnopqrstuvwxyz'
const reference = `${referenceType}-${referenceId}`
const memo = createKinMemo({ index, reference })
const parsed = KinMemo.fromB64String(memo)

expect(memo).toEqual('BQQAXLfhn7vy3z9pth17+SGIOSaZeSqquS67/TLIAQA=')
expect(parsed.transactionType()).toEqual(TransactionType.None)
expect(parsed.foreignKey().toString('base64')).toEqual('1234567890+abcdefghijklmnopqrstuvwxyAAA=')
expect(parsed.index()).toEqual(1)
})

it('fail creating a memo with reference data that is too long', () => {
const referenceType = '1234567890'
const referenceId = 'abcdefghijklmnopqrstuvwxyz1234'
const reference = `${referenceType}-${referenceId}`

try {
createKinMemo({ index, reference })
} catch (e) {
expect(e.message).toEqual('invalid foreign key length')
}
})
})
8 changes: 4 additions & 4 deletions libs/solana/src/lib/kin/create-kin.memo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import { TransactionType } from './transaction-type'

export function createKinMemo({
index,
memo,
reference,
type = TransactionType.None,
}: {
index: number
memo?: string
reference?: string
type?: TransactionType
}) {
let data = Buffer.alloc(29)

if (memo) {
data = Buffer.from(memo, 'base64')
if (reference) {
data = Buffer.from(reference, 'base64')
}
const kinMemo = KinMemo.new(1, type, index, data)

Expand Down
2 changes: 2 additions & 0 deletions libs/solana/src/lib/kin/generate-kin-memo-instruction.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { createReferenceKey } from '../helpers/create-reference-key'
import { generateKinMemoInstruction, MEMO_V1_TOKEN_ID } from './generate-kin-memo-instruction'
import { TransactionType } from './transaction-type'

describe('generateMemoInstruction', () => {
it('should create a Memo Instruction', async () => {
const memo = generateKinMemoInstruction({
index: 1,
reference: createReferenceKey({ type: 'ref-type', id: 'ref-id' }),
type: TransactionType.Spend,
})

Expand Down
Loading

0 comments on commit 1a4f251

Please sign in to comment.