@solana/pay
Advanced tools
Comparing version 0.2.0 to 0.2.1
@@ -19,2 +19,3 @@ "use strict"; | ||
const bignumber_js_1 = __importDefault(require("bignumber.js")); | ||
const constants_1 = require("./constants"); | ||
/** | ||
@@ -51,17 +52,21 @@ * Thrown when a transaction doesn't contain a valid Solana Pay transfer. | ||
throw meta.err; | ||
if (reference && !Array.isArray(reference)) { | ||
reference = [reference]; | ||
} | ||
const [preAmount, postAmount] = splToken | ||
? yield validateSPLTokenTransfer(message, meta, recipient, splToken) | ||
: yield validateSystemTransfer(message, meta, recipient); | ||
? yield validateSPLTokenTransfer(message, meta, recipient, splToken, reference) | ||
: yield validateSystemTransfer(message, meta, recipient, reference); | ||
if (postAmount.minus(preAmount).lt(amount)) | ||
throw new ValidateTransferError('amount not transferred'); | ||
if (reference) { | ||
if (!Array.isArray(reference)) { | ||
reference = [reference]; | ||
} | ||
for (const pubkey of reference) { | ||
if (!message.accountKeys.some((accountKey) => accountKey.equals(pubkey))) | ||
throw new ValidateTransferError('reference not found'); | ||
} | ||
if (memo) { | ||
// Check that the second instruction is a memo instruction with the expected memo. | ||
const transaction = web3_js_1.Transaction.populate(message); | ||
const instruction = transaction.instructions[1]; | ||
if (!instruction) | ||
throw new ValidateTransferError('missing memo instruction'); | ||
if (!instruction.programId.equals(constants_1.MEMO_PROGRAM_ID)) | ||
throw new ValidateTransferError('invalid memo program'); | ||
if (!instruction.data.equals(Buffer.from(memo, 'utf8'))) | ||
throw new ValidateTransferError('invalid memo'); | ||
} | ||
// FIXME: add memo check | ||
return response; | ||
@@ -71,4 +76,19 @@ }); | ||
exports.validateTransfer = validateTransfer; | ||
function validateSystemTransfer(message, meta, recipient) { | ||
function validateSystemTransfer(message, meta, recipient, references) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (references) { | ||
// Check that the first instruction is a system transfer instruction. | ||
const transaction = web3_js_1.Transaction.populate(message); | ||
const instruction = transaction.instructions[0]; | ||
web3_js_1.SystemInstruction.decodeTransfer(instruction); | ||
// Check that the expected reference keys exactly match the extra keys provided to the instruction. | ||
const [_from, _to, ...extraKeys] = instruction.keys; | ||
const length = extraKeys.length; | ||
if (length !== references.length) | ||
throw new ValidateTransferError('invalid references'); | ||
for (let i = 0; i < length; i++) { | ||
if (!extraKeys[i].pubkey.equals(references[i])) | ||
throw new ValidateTransferError(`invalid reference ${i}`); | ||
} | ||
} | ||
const accountIndex = message.accountKeys.findIndex((pubkey) => pubkey.equals(recipient)); | ||
@@ -83,5 +103,21 @@ if (accountIndex === -1) | ||
} | ||
function validateSPLTokenTransfer(message, meta, recipient, splToken) { | ||
function validateSPLTokenTransfer(message, meta, recipient, splToken, references) { | ||
var _a, _b; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (references) { | ||
// Check that the first instruction is an SPL token transfer instruction. | ||
const transaction = web3_js_1.Transaction.populate(message); | ||
const instruction = (0, spl_token_1.decodeInstruction)(transaction.instructions[0]); | ||
if (!(0, spl_token_1.isTransferCheckedInstruction)(instruction) && !(0, spl_token_1.isTransferInstruction)(instruction)) | ||
throw new ValidateTransferError('invalid transfer'); | ||
// Check that the expected reference keys exactly match the extra keys provided to the instruction. | ||
const extraKeys = instruction.keys.multiSigners; | ||
const length = extraKeys.length; | ||
if (length !== references.length) | ||
throw new ValidateTransferError('invalid references'); | ||
for (let i = 0; i < length; i++) { | ||
if (!extraKeys[i].pubkey.equals(references[i])) | ||
throw new ValidateTransferError(`invalid reference ${i}`); | ||
} | ||
} | ||
const recipientATA = yield (0, spl_token_1.getAssociatedTokenAddress)(splToken, recipient); | ||
@@ -88,0 +124,0 @@ const accountIndex = message.accountKeys.findIndex((pubkey) => pubkey.equals(recipientATA)); |
{ | ||
"name": "@solana/pay", | ||
"version": "0.2.0", | ||
"version": "0.2.1", | ||
"author": "Solana Maintainers <maintainers@solana.foundation>", | ||
@@ -48,24 +48,25 @@ "repository": "https://github.com/solana-labs/solana-pay", | ||
"devDependencies": { | ||
"@types/eslint": "^8.2.1", | ||
"@types/eslint": "^8.4.5", | ||
"@types/eslint-plugin-prettier": "^3.1.0", | ||
"@types/jest": "^27.4.0", | ||
"@types/node": "^16.11.14", | ||
"@types/prettier": "^2.4.2", | ||
"@typescript-eslint/eslint-plugin": "^5.14.0", | ||
"@typescript-eslint/parser": "^5.14.0", | ||
"eslint": "^8.6.0", | ||
"@types/jest": "^28.1.6", | ||
"@types/node": "^18.6.2", | ||
"@types/node-fetch": "^2.6.2", | ||
"@types/prettier": "^2.6.4", | ||
"@typescript-eslint/eslint-plugin": "^5.31.0", | ||
"@typescript-eslint/parser": "^5.31.0", | ||
"eslint": "^8.20.0", | ||
"eslint-config-prettier": "^8.3.0", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"gh-pages": "^3.2.3", | ||
"jest": "^27.4.7", | ||
"prettier": "^2.5.1", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"gh-pages": "^4.0.0", | ||
"jest": "^28.1.3", | ||
"prettier": "^2.7.1", | ||
"shx": "^0.3.3", | ||
"ts-jest": "^27.1.2", | ||
"ts-node": "^10.7.0", | ||
"ts-jest": "^28.0.7", | ||
"ts-node": "^10.9.1", | ||
"tsc-esm": "^1.0.4", | ||
"tslib": "^2.3.1", | ||
"typedoc": "^0.22.13", | ||
"typescript": "^4.5.4", | ||
"tslib": "^2.4.0", | ||
"typedoc": "^0.23.9", | ||
"typescript": "^4.7.4", | ||
"typescript-esm": "^2.0.0" | ||
} | ||
} |
@@ -33,4 +33,17 @@ # Solana Pay | ||
- [Merchant Integration](https://docs.solanapay.com/core/merchant-integration) | ||
- [Wallet Integration](https://docs.solanapay.com/core/wallet-integration) | ||
### Transaction Requests | ||
A Solana Pay transaction request URL describes an interactive request for any Solana transaction. The parameters in the URL are used by a wallet to make an HTTP request to compose any transaction. | ||
- [Create a transaction request](https://docs.solanapay.com/core/transaction-request/merchant-integration) | ||
### Transfer Requests | ||
A Solana Pay transfer request URL describes a non-interactive request for a SOL or SPL Token transfer. The parameters in the URL are used by a wallet to directly compose the transaction. | ||
- [Create a transfer request](https://docs.solanapay.com/core/transfer-request/merchant-integration) | ||
- [Handle a transfer request](https://docs.solanapay.com/core/transfer-request/wallet-integration) | ||
## Other resources | ||
- [API Reference](https://docs.solanapay.com/api/core) | ||
@@ -37,0 +50,0 @@ - [Brand Guidelines](https://solanapay.com/branding) |
@@ -1,3 +0,8 @@ | ||
import { getAssociatedTokenAddress } from '@solana/spl-token'; | ||
import { | ||
decodeInstruction, | ||
getAssociatedTokenAddress, | ||
isTransferCheckedInstruction, | ||
isTransferInstruction, | ||
} from '@solana/spl-token'; | ||
import { | ||
ConfirmedTransactionMeta, | ||
@@ -8,2 +13,4 @@ Connection, | ||
Message, | ||
SystemInstruction, | ||
Transaction, | ||
TransactionResponse, | ||
@@ -13,3 +20,4 @@ TransactionSignature, | ||
import BigNumber from 'bignumber.js'; | ||
import { Amount, Memo, Recipient, References, SPLToken } from './types'; | ||
import { MEMO_PROGRAM_ID } from './constants'; | ||
import { Amount, Memo, Recipient, Reference, References, SPLToken } from './types'; | ||
@@ -63,21 +71,20 @@ /** | ||
if (reference && !Array.isArray(reference)) { | ||
reference = [reference]; | ||
} | ||
const [preAmount, postAmount] = splToken | ||
? await validateSPLTokenTransfer(message, meta, recipient, splToken) | ||
: await validateSystemTransfer(message, meta, recipient); | ||
? await validateSPLTokenTransfer(message, meta, recipient, splToken, reference) | ||
: await validateSystemTransfer(message, meta, recipient, reference); | ||
if (postAmount.minus(preAmount).lt(amount)) throw new ValidateTransferError('amount not transferred'); | ||
if (reference) { | ||
if (!Array.isArray(reference)) { | ||
reference = [reference]; | ||
} | ||
for (const pubkey of reference) { | ||
if (!message.accountKeys.some((accountKey) => accountKey.equals(pubkey))) | ||
throw new ValidateTransferError('reference not found'); | ||
} | ||
if (memo) { | ||
// Check that the second instruction is a memo instruction with the expected memo. | ||
const transaction = Transaction.populate(message); | ||
const instruction = transaction.instructions[1]; | ||
if (!instruction) throw new ValidateTransferError('missing memo instruction'); | ||
if (!instruction.programId.equals(MEMO_PROGRAM_ID)) throw new ValidateTransferError('invalid memo program'); | ||
if (!instruction.data.equals(Buffer.from(memo, 'utf8'))) throw new ValidateTransferError('invalid memo'); | ||
} | ||
// FIXME: add memo check | ||
return response; | ||
@@ -89,4 +96,21 @@ } | ||
meta: ConfirmedTransactionMeta, | ||
recipient: Recipient | ||
recipient: Recipient, | ||
references?: Reference[] | ||
): Promise<[BigNumber, BigNumber]> { | ||
if (references) { | ||
// Check that the first instruction is a system transfer instruction. | ||
const transaction = Transaction.populate(message); | ||
const instruction = transaction.instructions[0]; | ||
SystemInstruction.decodeTransfer(instruction); | ||
// Check that the expected reference keys exactly match the extra keys provided to the instruction. | ||
const [_from, _to, ...extraKeys] = instruction.keys; | ||
const length = extraKeys.length; | ||
if (length !== references.length) throw new ValidateTransferError('invalid references'); | ||
for (let i = 0; i < length; i++) { | ||
if (!extraKeys[i].pubkey.equals(references[i])) throw new ValidateTransferError(`invalid reference ${i}`); | ||
} | ||
} | ||
const accountIndex = message.accountKeys.findIndex((pubkey) => pubkey.equals(recipient)); | ||
@@ -105,4 +129,22 @@ if (accountIndex === -1) throw new ValidateTransferError('recipient not found'); | ||
recipient: Recipient, | ||
splToken: SPLToken | ||
splToken: SPLToken, | ||
references?: Reference[] | ||
): Promise<[BigNumber, BigNumber]> { | ||
if (references) { | ||
// Check that the first instruction is an SPL token transfer instruction. | ||
const transaction = Transaction.populate(message); | ||
const instruction = decodeInstruction(transaction.instructions[0]); | ||
if (!isTransferCheckedInstruction(instruction) && !isTransferInstruction(instruction)) | ||
throw new ValidateTransferError('invalid transfer'); | ||
// Check that the expected reference keys exactly match the extra keys provided to the instruction. | ||
const extraKeys = instruction.keys.multiSigners; | ||
const length = extraKeys.length; | ||
if (length !== references.length) throw new ValidateTransferError('invalid references'); | ||
for (let i = 0; i < length; i++) { | ||
if (!extraKeys[i].pubkey.equals(references[i])) throw new ValidateTransferError(`invalid reference ${i}`); | ||
} | ||
} | ||
const recipientATA = await getAssociatedTokenAddress(splToken, recipient); | ||
@@ -109,0 +151,0 @@ const accountIndex = message.accountKeys.findIndex((pubkey) => pubkey.equals(recipientATA)); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
154401
2163
55
1
22