Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

Commit

Permalink
Add spl-memo typescript implementation (#2583)
Browse files Browse the repository at this point in the history
* Memo ts implementation

Co-authored by: Bryan Ischo <bryan@ischo.com>

* Add building and CI

* Remove unneeded mocha file

* Update Node version

* Add chai dependency

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
  • Loading branch information
Tyera Eulberg and joncinque authored Jun 14, 2022
1 parent aaca535 commit 11ef88a
Show file tree
Hide file tree
Showing 13 changed files with 3,793 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/pull-request-memo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,19 @@ jobs:
- name: Build and test
run: ./ci/cargo-test-bpf.sh memo

js-test:
runs-on: ubuntu-latest
env:
NODE_VERSION: 16.x
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v1
with:
node-version: ${{ env.NODE_VERSION }}
- uses: actions/cache@v2
with:
path: ~/.cache/yarn
key: yarn-${{ hashFiles('memo/ts/yarn.lock') }}
- run: ./ci/ts-test-memo.sh
12 changes: 12 additions & 0 deletions ci/ts-test-memo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash

set -e
cd "$(dirname "$0")/.."
source ./ci/solana-version.sh install

set -x
cd memo/ts
yarn
yarn build
yarn lint
yarn test
1 change: 1 addition & 0 deletions memo/ts/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
12 changes: 12 additions & 0 deletions memo/ts/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
],
};
1 change: 1 addition & 0 deletions memo/ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
6 changes: 6 additions & 0 deletions memo/ts/.prettierrc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
arrowParens: "avoid"
bracketSpacing: false
semi: true
singleQuote: true
tabWidth: 2
trailingComma: "all"
5 changes: 5 additions & 0 deletions memo/ts/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
56 changes: 56 additions & 0 deletions memo/ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "@solana/spl-memo",
"version": "0.1.0",
"description": "SPL Memo Program JS API",
"files": [
"dist",
"src"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"sideEffects": false,
"scripts": {
"build": "npm run clean && tsc",
"clean": "rimraf ./dist",
"lint": "eslint . && npm run pretty",
"lint:fix": "eslint . --fix && npm run pretty:fix",
"pretty": "prettier --check '{,{src,test}/**/}*.{j,t}s'",
"pretty:fix": "prettier --write '{,{src,test}/**/}*.{j,t}s'",
"test": "npm run test:unit && npm run test:e2e",
"test:unit": "jest test/unit",
"test:e2e": "start-server-and-test 'solana-test-validator -r -q' http://localhost:8899/health 'jest test/e2e'"
},
"repository": {
"type": "git",
"url": "https://github.com/solana-labs/solana-program-library"
},
"publishConfig": {
"access": "public"
},
"author": "Solana Maintainers <maintainers@solana.com>",
"license": "MIT",
"devDependencies": {
"@types/chai": "^4.3.1",
"@types/jest": "^28.1.1",
"@types/node": "^17.0.42",
"@types/node-fetch": "^2.6.1",
"@types/prettier": "^2.6.3",
"@typescript-eslint/eslint-plugin": "^5.28.0",
"@typescript-eslint/parser": "^5.28.0",
"chai": "^4.3.6",
"eslint": "^8.17.0",
"eslint-config-prettier": "^8.5.0",
"jest": "^28.1.1",
"prettier": "^2.7.0",
"process": "^0.11.10",
"start-server-and-test": "^1.14.0",
"ts-jest": "^28.0.5",
"ts-node": "^10.8.1",
"typescript": "^4.7.3"
},
"dependencies": {
"@solana/web3.js": "^1.41.0",
"buffer": "^6.0.3"
}
}
43 changes: 43 additions & 0 deletions memo/ts/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {Buffer} from 'buffer';
import {PublicKey, TransactionInstruction} from '@solana/web3.js';

export const MEMO_PROGRAM_ID: PublicKey = new PublicKey(
'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr',
);

/**
* Creates and returns an instruction which validates a string of UTF-8
* encoded characters and verifies that any accounts provided are signers of
* the transaction. The program also logs the memo, as well as any verified
* signer addresses, to the transaction log, so that anyone can easily observe
* memos and know they were approved by zero or more addresses by inspecting
* the transaction log from a trusted provider.
*
* Public keys passed in via the signerPubkeys will identify Signers which
* must subsequently sign the Transaction including the returned
* TransactionInstruction in order for the transaction to be valid.
*
* @param memo The UTF-8 encoded memo string to validate
* @param signerPubkeys An array of public keys which must sign the
* Transaction including the returned TransactionInstruction in order
* for the transaction to be valid and the memo verification to
* succeed. null is allowed if there are no signers for the memo
* verification.
**/
export function createMemoInstruction(
memo: string,
signerPubkeys?: Array<PublicKey>,
): TransactionInstruction {
const keys =
signerPubkeys == null
? []
: signerPubkeys.map(function (key) {
return {pubkey: key, isSigner: true, isWritable: false};
});

return new TransactionInstruction({
keys: keys,
programId: MEMO_PROGRAM_ID,
data: Buffer.from(memo, 'utf8'),
});
}
28 changes: 28 additions & 0 deletions memo/ts/test/e2e/transaction.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {createMemoInstruction} from '../../src';
import {
Connection,
Keypair,
Transaction,
LAMPORTS_PER_SOL,
sendAndConfirmTransaction,
} from '@solana/web3.js';

test('transaction: live', async () => {
const url = 'http://localhost:8899';
const connection = new Connection(url, 'confirmed');
await connection.getVersion();
const signer = new Keypair(); // also fee-payer

const airdropSignature = await connection.requestAirdrop(
signer.publicKey,
LAMPORTS_PER_SOL / 10,
);
await connection.confirmTransaction(airdropSignature, 'confirmed');

const memoTx = new Transaction().add(
createMemoInstruction('this is a test memo', [signer.publicKey]),
);
await sendAndConfirmTransaction(connection, memoTx, [signer], {
preflightCommitment: 'confirmed',
});
});
41 changes: 41 additions & 0 deletions memo/ts/test/unit/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {createMemoInstruction, MEMO_PROGRAM_ID} from '../../src';
import {expect} from 'chai';
import {Keypair} from '@solana/web3.js';

test('instruction: no signers', () => {
const ix = createMemoInstruction('this is a test memo', []);
expect(ix.programId).to.eql(MEMO_PROGRAM_ID);
expect(ix.keys).to.have.length(0);
expect(ix.data).to.have.length(19);

const ix2 = createMemoInstruction('this is a test');
expect(ix2.programId).to.eql(MEMO_PROGRAM_ID);
expect(ix2.keys).to.have.length(0);
expect(ix2.data).to.have.length(14);
});

test('instruction: one signer', () => {
const signer = new Keypair();
const ix = createMemoInstruction('this is a test memo', [signer.publicKey]);
expect(ix.programId).to.eql(MEMO_PROGRAM_ID);
expect(ix.keys).to.have.length(1);
expect(ix.data).to.have.length(19);
});

test('instruction: many signers', () => {
const signer0 = new Keypair();
const signer1 = new Keypair();
const signer2 = new Keypair();
const signer3 = new Keypair();
const signer4 = new Keypair();
const ix = createMemoInstruction('this is a test memo', [
signer0.publicKey,
signer1.publicKey,
signer2.publicKey,
signer3.publicKey,
signer4.publicKey,
]);
expect(ix.programId).to.eql(MEMO_PROGRAM_ID);
expect(ix.keys).to.have.length(5);
expect(ix.data).to.have.length(19);
});
16 changes: 16 additions & 0 deletions memo/ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
"outDir": "dist",
"declaration": true,
"declarationMap": true,
"strict": true,
"isolatedModules": true,
"noImplicitReturns": true,
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
Loading

0 comments on commit 11ef88a

Please sign in to comment.