Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@code-wallet/client",
"version": "1.0.4",
"version": "1.1.0",
"license": "MIT",
"repository": {
"type": "git",
Expand All @@ -22,7 +22,7 @@
"maintained node versions"
],
"dependencies": {
"@code-wallet/library": "^1.0.4",
"@code-wallet/library": "^1.1.0",
"bs58": "^5.0.0",
"buffer": "6.0.3"
},
Expand Down
18 changes: 15 additions & 3 deletions packages/client/src/intents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import { Buffer } from "buffer";
import {
IntentOptions,
PaymentRequestIntent,
PaymentRequestWithLoginIntent,
PaymentRequestOptions,
WebhookParams
LoginRequestOptions,
WebhookParams,
Intent,
} from "@code-wallet/library";
import { Connection } from "./connection";

Expand All @@ -19,7 +22,11 @@ enum PaymentIntentState {
Confirmed = 'confirmed',
}

type CreatePaymentIntentOptions = IntentOptions & PaymentRequestOptions & Partial<WebhookParams>;
type CreatePaymentIntentOptions = PaymentRequestOptions &
IntentOptions &
Partial<WebhookParams> &
Partial<LoginRequestOptions>;

type GetStatusForIntentOptions = { intent: string };

const pending = { status: PaymentIntentState.Pending };
Expand All @@ -39,7 +46,12 @@ const paymentIntents = {
create: async (obj: CreatePaymentIntentOptions) => {
obj.mode = 'payment';

const intent = new PaymentRequestIntent(obj);
let intent : Intent;
if (obj.login) {
intent = new PaymentRequestWithLoginIntent(obj);
} else {
intent = new PaymentRequestIntent(obj);
}

const envelope = intent.sign();
const body = {
Expand Down
4 changes: 2 additions & 2 deletions packages/library/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@code-wallet/library",
"version": "1.0.5",
"version": "1.1.0",
"license": "MIT",
"repository": {
"type": "git",
Expand All @@ -22,7 +22,7 @@
"maintained node versions"
],
"dependencies": {
"@code-wallet/rpc": "^1.0.0",
"@code-wallet/rpc": "^1.1.0",
"@noble/curves": "^1.2.0",
"@noble/hashes": "^1.3.0",
"bs58": "^5.0.0",
Expand Down
17 changes: 16 additions & 1 deletion packages/library/src/elements/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type ElementLocale = string;
* Defines the options needed for processing a payment request.
*/
interface PaymentRequestOptions {
/** The destination identifier, e.g., an account or user ID. */
/** The destination token address as a base58 string. */
destination: string;

/** The monetary amount for the payment request. */
Expand All @@ -31,6 +31,19 @@ interface PaymentRequestOptions {
currency: CurrencyCode;
}

/**
* Defines the options needed for processing a login request from a third-party.
*/
interface LoginRequestOptions {
login: {
/** The hostname of a third-party that is requesting this login. This hostname must serve a /.well-known/code-payments.json file with the verifier public key. */
domain: string;

/** The public key of the verifier that sign the login request as a base58 string. This public key must be found in the well-known file. */
verifier: string;
};
}

/**
* Describes the locale options for the element.
*/
Expand All @@ -53,6 +66,7 @@ interface AppearanceOptions {
*/
type ElementOptions = Partial<IntentOptions> &
Partial<PaymentRequestOptions> &
Partial<LoginRequestOptions> &
Partial<LocaleOptions> &
Partial<AppearanceOptions> &
Partial<WebhookParams> &
Expand All @@ -68,4 +82,5 @@ export type {
LocaleOptions,
AppearanceOptions,
PaymentRequestOptions,
LoginRequestOptions,
};
56 changes: 55 additions & 1 deletion packages/library/src/elements/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import {
ErrDestinationRequired,
ErrInvalidCurrency,
ErrInvalidMode,
ErrLoginRequired,
ErrLoginDomainRequired,
ErrLoginVerifierRequired,
ErrNotImplemented,
ErrInvalidValue,
} from '../errors';
import { PublicKey } from '../keys';
import { Keypair, PublicKey } from '../keys';

/**
* Validates the properties of the given `ElementOptions` for intents.
Expand Down Expand Up @@ -58,6 +63,45 @@ function validatePaymentRequestOptions(intent: ElementOptions) {
PublicKey.fromBase58(intent.destination);
}

/**
* Validates the properties of the given `ElementOptions` for login requests.
*
* @param intent The options to validate.
* @throws {ErrLoginRequired} If the `login` property is undefined.
* @throws {ErrLoginDomainRequired} If the `login.domain` property is undefined.
* @throws {ErrLoginVerifierRequired} If the `login.verifier` property is undefined.
* @throws {ErrInvalidAddress} If the `login.verifier` property is not a valid base58 address.
*/
function validateLoginRequestOptions(intent: ElementOptions) {
if (intent.login === undefined) {
throw ErrLoginRequired();
}

if (intent.login.domain === undefined) {
throw ErrLoginDomainRequired();
}

if (intent.login.verifier === undefined) {
throw ErrLoginVerifierRequired();
}

// Validate that the verifier is a valid address.
PublicKey.fromBase58(intent.login.verifier);
}

/**
* Validates the properties of the given `ElementOptions` for signers.
*/
function validateSigners(intent: ElementOptions) {
if (!intent.signers) { return; }

for (const signer of intent.signers) {
if (!(signer instanceof Keypair)) {
throw ErrInvalidValue();
}
}
}

/**
* Validates the properties of the given `ElementOptions` depending on its mode.
*
Expand All @@ -68,8 +112,18 @@ function validateElementOptions(intent: ElementOptions) {
validateIntentOptions(intent);

switch (intent.mode) {
case 'login':
throw ErrNotImplemented(); // TODO: implement login (soon)
break;
case 'payment':
validatePaymentRequestOptions(intent);

if (intent.login) {
validateLoginRequestOptions(intent);
}
if (intent.signers) {
validateSigners(intent);
}
break;
default:
throw ErrInvalidMode();
Expand Down
10 changes: 10 additions & 0 deletions packages/library/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
const ErrNotImplemented = () => new Error("not implemented");
const ErrInvalidSize = () => new Error("invalid size");
const ErrDestinationRequired = () => new Error("destination is required");
const ErrAmountRequired = () => new Error("amount is required");
const ErrCurrencyRequired = () => new Error("currency is required");
const ErrInvalidCurrency = () => new Error("invalid currency");
const ErrUnexpectedError = () => new Error("unexpected error");
const ErrAmbiguousNonce = () => new Error("cannot derive nonce from both clientSecret and idempotencyKey");
const ErrInvalidValue = () => new Error("invalid value");
const ErrInvalidMode = () => new Error(`invalid mode`);
const ErrInvalidAddress = () => new Error("invalid address");
const ErrLoginRequired = () => new Error("login is required");
const ErrLoginDomainRequired = () => new Error("login domain is required");
const ErrLoginVerifierRequired = () => new Error("login verifier is required");

export {
ErrNotImplemented,
ErrInvalidSize,
ErrDestinationRequired,
ErrAmountRequired,
ErrCurrencyRequired,
ErrInvalidCurrency,
ErrUnexpectedError,
ErrAmbiguousNonce,
ErrInvalidValue,
ErrInvalidMode,
ErrInvalidAddress,
ErrLoginRequired,
ErrLoginDomainRequired,
ErrLoginVerifierRequired,
};
18 changes: 15 additions & 3 deletions packages/library/src/intent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as proto from '@code-wallet/rpc';
import { IdempotencyKey } from './idempotency';
import { ElementOptions } from './elements/options';
import { Keypair } from './keys';

export type IntentType = 'payment';
export type IntentType = 'payment' | 'login';

/**
* Options for creating an intent.
Expand All @@ -22,6 +23,11 @@ export interface IntentOptions {
* See https://code-wallet.github.io/code-sdk/docs/reference/idempotency.html for more information.
*/
idempotencyKey?: string;

/**
* A list of signers for an intent.
*/
signers?: Keypair[];
}

/**
Expand Down Expand Up @@ -60,8 +66,9 @@ export interface Intent {
toProto(): proto.Message;

/**
* Sign this intent with the rendezvous private key, returning an intent
* that is ready to be sent to the Code Sequencer.
* Sign this intent with the signer keys (if needed), returning an intent
* that is ready to be sent to the Code Sequencer. The intent will be signed
* with the rendezvous keypair by default, so no need to pass it in.
*
* @returns {SignedIntent} The signed intent.
*/
Expand All @@ -73,6 +80,11 @@ export interface Intent {
* @returns {string} The client secret for this intent.
*/
getClientSecret(): string;

/**
* Get the intent ID for this intent, which can be used to create linked browser elements or request status.
*/
getIntentId(): string;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class PaymentRequestIntent implements Intent {
*
* @returns The protobuf representation of the payment request intent.
*/
toProto() {
toProto() : proto.Message {
const destination = PublicKey.fromBase58(this.options.destination!);
const { currency, amount } = this.options;

Expand Down
Loading