@cartridge/controller 0.13.5 → 0.13.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$deps.log +27 -17
- package/.turbo/turbo-build.log +25 -15
- package/HEADLESS_MODE.md +28 -7
- package/dist/controller.d.ts +2 -1
- package/dist/index-CYAUAqql.js +1072 -0
- package/dist/index-CYAUAqql.js.map +1 -0
- package/dist/index.js +1925 -2488
- package/dist/index.js.map +1 -1
- package/dist/lookup.d.ts +2 -0
- package/dist/node/index.cjs +3 -3
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js +4 -4
- package/dist/node/index.js.map +1 -1
- package/dist/session/provider.d.ts +7 -2
- package/dist/session.js +141 -142
- package/dist/session.js.map +1 -1
- package/dist/stats.html +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/utils.d.ts +5 -3
- package/package.json +2 -2
- package/src/__tests__/lookupUsername.test.ts +166 -0
- package/src/__tests__/toWasmPolicies.test.ts +89 -40
- package/src/controller.ts +13 -0
- package/src/iframe/keychain.ts +7 -0
- package/src/lookup.ts +170 -2
- package/src/session/provider.ts +75 -48
- package/src/types.ts +6 -0
- package/src/utils.ts +19 -5
- package/dist/provider-NKp7_oNj.js +0 -387
- package/dist/provider-NKp7_oNj.js.map +0 -1
package/dist/types.d.ts
CHANGED
|
@@ -70,6 +70,11 @@ export interface LookupResult {
|
|
|
70
70
|
export interface LookupResponse {
|
|
71
71
|
results: LookupResult[];
|
|
72
72
|
}
|
|
73
|
+
export interface HeadlessUsernameLookupResult {
|
|
74
|
+
username: string;
|
|
75
|
+
exists: boolean;
|
|
76
|
+
signers: AuthOption[];
|
|
77
|
+
}
|
|
73
78
|
export declare enum FeeSource {
|
|
74
79
|
PAYMASTER = "PAYMASTER",
|
|
75
80
|
CREDITS = "CREDITS"
|
package/dist/utils.d.ts
CHANGED
|
@@ -8,13 +8,15 @@ export declare function normalizeCalls(calls: Call | Call[]): {
|
|
|
8
8
|
contractAddress: string;
|
|
9
9
|
calldata: import('starknet').HexCalldata;
|
|
10
10
|
}[];
|
|
11
|
+
export declare function getPresetSessionPolicies(config: Record<string, unknown>, chainId: string): SessionPolicies | undefined;
|
|
11
12
|
export declare function toSessionPolicies(policies: Policies): SessionPolicies;
|
|
12
13
|
/**
|
|
13
14
|
* Converts parsed session policies to WASM-compatible Policy objects.
|
|
14
15
|
*
|
|
15
|
-
* IMPORTANT: Policies are sorted canonically
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* IMPORTANT: Policies are sorted canonically and addresses are normalized
|
|
17
|
+
* via getChecksumAddress before hashing. Without this, Object.keys/entries
|
|
18
|
+
* reordering or inconsistent address casing can cause identical policies to
|
|
19
|
+
* produce different merkle roots, leading to "session/not-registered" errors.
|
|
18
20
|
* See: https://github.com/cartridge-gg/controller/issues/2357
|
|
19
21
|
*/
|
|
20
22
|
export declare function toWasmPolicies(policies: ParsedSessionPolicies): Policy[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cartridge/controller",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.7",
|
|
4
4
|
"description": "Cartridge Controller",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"vite-plugin-node-polyfills": "^0.23.0",
|
|
57
57
|
"vite-plugin-top-level-await": "^1.4.4",
|
|
58
58
|
"vite-plugin-wasm": "^3.4.1",
|
|
59
|
-
"@cartridge/tsconfig": "0.13.
|
|
59
|
+
"@cartridge/tsconfig": "0.13.7"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build:deps": "pnpm build",
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { constants } from "starknet";
|
|
2
|
+
import ControllerProvider from "../controller";
|
|
3
|
+
import { IMPLEMENTED_AUTH_OPTIONS } from "../types";
|
|
4
|
+
|
|
5
|
+
describe("lookupUsername", () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
(global as any).fetch = jest.fn();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.resetAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("returns normalized signer options in canonical order", async () => {
|
|
15
|
+
(global.fetch as jest.Mock).mockResolvedValue({
|
|
16
|
+
ok: true,
|
|
17
|
+
json: async () => ({
|
|
18
|
+
data: {
|
|
19
|
+
account: {
|
|
20
|
+
username: "alice",
|
|
21
|
+
controllers: {
|
|
22
|
+
edges: [
|
|
23
|
+
{
|
|
24
|
+
node: {
|
|
25
|
+
signers: [
|
|
26
|
+
{
|
|
27
|
+
isOriginal: true,
|
|
28
|
+
isRevoked: false,
|
|
29
|
+
metadata: {
|
|
30
|
+
__typename: "Eip191Credentials",
|
|
31
|
+
eip191: [
|
|
32
|
+
{ provider: "metamask", ethAddress: "0x1" },
|
|
33
|
+
{ provider: "google", ethAddress: "0x2" },
|
|
34
|
+
{ provider: "unknown", ethAddress: "0x3" },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
isOriginal: true,
|
|
40
|
+
isRevoked: false,
|
|
41
|
+
metadata: { __typename: "PasswordCredentials" },
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
isOriginal: true,
|
|
45
|
+
isRevoked: false,
|
|
46
|
+
metadata: { __typename: "WebauthnCredentials" },
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
isOriginal: true,
|
|
50
|
+
isRevoked: true,
|
|
51
|
+
metadata: { __typename: "WebauthnCredentials" },
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
isOriginal: true,
|
|
55
|
+
isRevoked: false,
|
|
56
|
+
metadata: {
|
|
57
|
+
__typename: "Eip191Credentials",
|
|
58
|
+
eip191: [{ provider: "discord", ethAddress: "0x4" }],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const provider = new ControllerProvider({ lazyload: true });
|
|
72
|
+
const result = await provider.lookupUsername("alice");
|
|
73
|
+
const expectedOrder = IMPLEMENTED_AUTH_OPTIONS.filter((option) =>
|
|
74
|
+
["google", "webauthn", "discord", "password", "metamask"].includes(
|
|
75
|
+
option,
|
|
76
|
+
),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(result).toEqual({
|
|
80
|
+
username: "alice",
|
|
81
|
+
exists: true,
|
|
82
|
+
signers: expectedOrder,
|
|
83
|
+
});
|
|
84
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
85
|
+
"https://api.cartridge.gg/query",
|
|
86
|
+
expect.any(Object),
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("filters non-original signers on non-mainnet chains", async () => {
|
|
91
|
+
(global.fetch as jest.Mock).mockResolvedValue({
|
|
92
|
+
ok: true,
|
|
93
|
+
json: async () => ({
|
|
94
|
+
data: {
|
|
95
|
+
account: {
|
|
96
|
+
username: "alice",
|
|
97
|
+
controllers: {
|
|
98
|
+
edges: [
|
|
99
|
+
{
|
|
100
|
+
node: {
|
|
101
|
+
signers: [
|
|
102
|
+
{
|
|
103
|
+
isOriginal: false,
|
|
104
|
+
isRevoked: false,
|
|
105
|
+
metadata: {
|
|
106
|
+
__typename: "Eip191Credentials",
|
|
107
|
+
eip191: [{ provider: "google", ethAddress: "0x1" }],
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
isOriginal: true,
|
|
112
|
+
isRevoked: false,
|
|
113
|
+
metadata: { __typename: "PasswordCredentials" },
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const provider = new ControllerProvider({
|
|
126
|
+
lazyload: true,
|
|
127
|
+
defaultChainId: constants.StarknetChainId.SN_SEPOLIA,
|
|
128
|
+
});
|
|
129
|
+
const result = await provider.lookupUsername("alice");
|
|
130
|
+
|
|
131
|
+
expect(result.signers).toEqual(["password"]);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("returns exists=false for unknown usernames", async () => {
|
|
135
|
+
(global.fetch as jest.Mock).mockResolvedValue({
|
|
136
|
+
ok: true,
|
|
137
|
+
json: async () => ({
|
|
138
|
+
data: {
|
|
139
|
+
account: null,
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const provider = new ControllerProvider({ lazyload: true });
|
|
145
|
+
const result = await provider.lookupUsername("missing-user");
|
|
146
|
+
|
|
147
|
+
expect(result).toEqual({
|
|
148
|
+
username: "missing-user",
|
|
149
|
+
exists: false,
|
|
150
|
+
signers: [],
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("throws on network failures", async () => {
|
|
155
|
+
(global.fetch as jest.Mock).mockResolvedValue({
|
|
156
|
+
ok: false,
|
|
157
|
+
status: 503,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const provider = new ControllerProvider({ lazyload: true });
|
|
161
|
+
|
|
162
|
+
await expect(provider.lookupUsername("alice")).rejects.toThrow(
|
|
163
|
+
"HTTP error! status: 503",
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -1,16 +1,27 @@
|
|
|
1
|
+
import { getChecksumAddress } from "starknet";
|
|
1
2
|
import { toWasmPolicies } from "../utils";
|
|
2
3
|
import { ParsedSessionPolicies } from "../policies";
|
|
3
4
|
|
|
5
|
+
// Valid hex addresses for testing (short but valid for getChecksumAddress)
|
|
6
|
+
const ADDR_A = "0x0aaa";
|
|
7
|
+
const ADDR_B = "0x0bbb";
|
|
8
|
+
const ADDR_C = "0x0ccc";
|
|
9
|
+
|
|
10
|
+
// Pre-compute checksummed forms
|
|
11
|
+
const ADDR_A_CS = getChecksumAddress(ADDR_A);
|
|
12
|
+
const ADDR_B_CS = getChecksumAddress(ADDR_B);
|
|
13
|
+
const ADDR_C_CS = getChecksumAddress(ADDR_C);
|
|
14
|
+
|
|
4
15
|
describe("toWasmPolicies", () => {
|
|
5
16
|
describe("canonical ordering", () => {
|
|
6
17
|
test("sorts contracts by address regardless of input order", () => {
|
|
7
18
|
const policies1: ParsedSessionPolicies = {
|
|
8
19
|
verified: false,
|
|
9
20
|
contracts: {
|
|
10
|
-
|
|
21
|
+
[ADDR_A]: {
|
|
11
22
|
methods: [{ entrypoint: "foo", authorized: true }],
|
|
12
23
|
},
|
|
13
|
-
|
|
24
|
+
[ADDR_B]: {
|
|
14
25
|
methods: [{ entrypoint: "bar", authorized: true }],
|
|
15
26
|
},
|
|
16
27
|
},
|
|
@@ -19,10 +30,10 @@ describe("toWasmPolicies", () => {
|
|
|
19
30
|
const policies2: ParsedSessionPolicies = {
|
|
20
31
|
verified: false,
|
|
21
32
|
contracts: {
|
|
22
|
-
|
|
33
|
+
[ADDR_B]: {
|
|
23
34
|
methods: [{ entrypoint: "bar", authorized: true }],
|
|
24
35
|
},
|
|
25
|
-
|
|
36
|
+
[ADDR_A]: {
|
|
26
37
|
methods: [{ entrypoint: "foo", authorized: true }],
|
|
27
38
|
},
|
|
28
39
|
},
|
|
@@ -32,16 +43,16 @@ describe("toWasmPolicies", () => {
|
|
|
32
43
|
const result2 = toWasmPolicies(policies2);
|
|
33
44
|
|
|
34
45
|
expect(result1).toEqual(result2);
|
|
35
|
-
// First policy should be for
|
|
36
|
-
expect(result1[0]).toHaveProperty("target",
|
|
37
|
-
expect(result1[1]).toHaveProperty("target",
|
|
46
|
+
// First policy should be for ADDR_A (sorted alphabetically)
|
|
47
|
+
expect(result1[0]).toHaveProperty("target", ADDR_A_CS);
|
|
48
|
+
expect(result1[1]).toHaveProperty("target", ADDR_B_CS);
|
|
38
49
|
});
|
|
39
50
|
|
|
40
51
|
test("sorts methods within contracts by entrypoint", () => {
|
|
41
52
|
const policies1: ParsedSessionPolicies = {
|
|
42
53
|
verified: false,
|
|
43
54
|
contracts: {
|
|
44
|
-
|
|
55
|
+
[ADDR_A]: {
|
|
45
56
|
methods: [
|
|
46
57
|
{ entrypoint: "zebra", authorized: true },
|
|
47
58
|
{ entrypoint: "apple", authorized: true },
|
|
@@ -54,7 +65,7 @@ describe("toWasmPolicies", () => {
|
|
|
54
65
|
const policies2: ParsedSessionPolicies = {
|
|
55
66
|
verified: false,
|
|
56
67
|
contracts: {
|
|
57
|
-
|
|
68
|
+
[ADDR_A]: {
|
|
58
69
|
methods: [
|
|
59
70
|
{ entrypoint: "mango", authorized: true },
|
|
60
71
|
{ entrypoint: "zebra", authorized: true },
|
|
@@ -74,19 +85,19 @@ describe("toWasmPolicies", () => {
|
|
|
74
85
|
const policies1: ParsedSessionPolicies = {
|
|
75
86
|
verified: false,
|
|
76
87
|
contracts: {
|
|
77
|
-
|
|
88
|
+
[ADDR_C]: {
|
|
78
89
|
methods: [
|
|
79
90
|
{ entrypoint: "c_method", authorized: true },
|
|
80
91
|
{ entrypoint: "a_method", authorized: true },
|
|
81
92
|
],
|
|
82
93
|
},
|
|
83
|
-
|
|
94
|
+
[ADDR_A]: {
|
|
84
95
|
methods: [
|
|
85
96
|
{ entrypoint: "z_method", authorized: true },
|
|
86
97
|
{ entrypoint: "a_method", authorized: true },
|
|
87
98
|
],
|
|
88
99
|
},
|
|
89
|
-
|
|
100
|
+
[ADDR_B]: {
|
|
90
101
|
methods: [{ entrypoint: "b_method", authorized: true }],
|
|
91
102
|
},
|
|
92
103
|
},
|
|
@@ -96,16 +107,16 @@ describe("toWasmPolicies", () => {
|
|
|
96
107
|
const policies2: ParsedSessionPolicies = {
|
|
97
108
|
verified: false,
|
|
98
109
|
contracts: {
|
|
99
|
-
|
|
110
|
+
[ADDR_B]: {
|
|
100
111
|
methods: [{ entrypoint: "b_method", authorized: true }],
|
|
101
112
|
},
|
|
102
|
-
|
|
113
|
+
[ADDR_A]: {
|
|
103
114
|
methods: [
|
|
104
115
|
{ entrypoint: "a_method", authorized: true },
|
|
105
116
|
{ entrypoint: "z_method", authorized: true },
|
|
106
117
|
],
|
|
107
118
|
},
|
|
108
|
-
|
|
119
|
+
[ADDR_C]: {
|
|
109
120
|
methods: [
|
|
110
121
|
{ entrypoint: "a_method", authorized: true },
|
|
111
122
|
{ entrypoint: "c_method", authorized: true },
|
|
@@ -119,21 +130,21 @@ describe("toWasmPolicies", () => {
|
|
|
119
130
|
|
|
120
131
|
expect(result1).toEqual(result2);
|
|
121
132
|
|
|
122
|
-
// Verify order:
|
|
123
|
-
// Within
|
|
124
|
-
expect(result1[0]).toHaveProperty("target",
|
|
125
|
-
expect(result1[2]).toHaveProperty("target",
|
|
126
|
-
expect(result1[3]).toHaveProperty("target",
|
|
133
|
+
// Verify order: ADDR_A first, then ADDR_B, then ADDR_C
|
|
134
|
+
// Within ADDR_A: a_method before z_method
|
|
135
|
+
expect(result1[0]).toHaveProperty("target", ADDR_A_CS);
|
|
136
|
+
expect(result1[2]).toHaveProperty("target", ADDR_B_CS);
|
|
137
|
+
expect(result1[3]).toHaveProperty("target", ADDR_C_CS);
|
|
127
138
|
});
|
|
128
139
|
|
|
129
140
|
test("handles case-insensitive address sorting", () => {
|
|
130
141
|
const policies1: ParsedSessionPolicies = {
|
|
131
142
|
verified: false,
|
|
132
143
|
contracts: {
|
|
133
|
-
"
|
|
144
|
+
"0x0aaa": {
|
|
134
145
|
methods: [{ entrypoint: "foo", authorized: true }],
|
|
135
146
|
},
|
|
136
|
-
"
|
|
147
|
+
"0x0AAB": {
|
|
137
148
|
methods: [{ entrypoint: "bar", authorized: true }],
|
|
138
149
|
},
|
|
139
150
|
},
|
|
@@ -142,10 +153,10 @@ describe("toWasmPolicies", () => {
|
|
|
142
153
|
const policies2: ParsedSessionPolicies = {
|
|
143
154
|
verified: false,
|
|
144
155
|
contracts: {
|
|
145
|
-
"
|
|
156
|
+
"0x0AAB": {
|
|
146
157
|
methods: [{ entrypoint: "bar", authorized: true }],
|
|
147
158
|
},
|
|
148
|
-
"
|
|
159
|
+
"0x0aaa": {
|
|
149
160
|
methods: [{ entrypoint: "foo", authorized: true }],
|
|
150
161
|
},
|
|
151
162
|
},
|
|
@@ -157,6 +168,34 @@ describe("toWasmPolicies", () => {
|
|
|
157
168
|
expect(result1).toEqual(result2);
|
|
158
169
|
});
|
|
159
170
|
|
|
171
|
+
test("normalizes address casing via getChecksumAddress", () => {
|
|
172
|
+
const addr =
|
|
173
|
+
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";
|
|
174
|
+
const policies1: ParsedSessionPolicies = {
|
|
175
|
+
verified: false,
|
|
176
|
+
contracts: {
|
|
177
|
+
[addr.toLowerCase()]: {
|
|
178
|
+
methods: [{ entrypoint: "transfer", authorized: true }],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const policies2: ParsedSessionPolicies = {
|
|
184
|
+
verified: false,
|
|
185
|
+
contracts: {
|
|
186
|
+
[addr.toUpperCase().replace("0X", "0x")]: {
|
|
187
|
+
methods: [{ entrypoint: "transfer", authorized: true }],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const result1 = toWasmPolicies(policies1);
|
|
193
|
+
const result2 = toWasmPolicies(policies2);
|
|
194
|
+
|
|
195
|
+
expect(result1).toEqual(result2);
|
|
196
|
+
expect(result1[0]).toHaveProperty("target", getChecksumAddress(addr));
|
|
197
|
+
});
|
|
198
|
+
|
|
160
199
|
test("handles empty policies", () => {
|
|
161
200
|
const policies: ParsedSessionPolicies = {
|
|
162
201
|
verified: false,
|
|
@@ -179,15 +218,21 @@ describe("toWasmPolicies", () => {
|
|
|
179
218
|
});
|
|
180
219
|
|
|
181
220
|
describe("ApprovalPolicy handling", () => {
|
|
221
|
+
const TOKEN =
|
|
222
|
+
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";
|
|
223
|
+
const TOKEN_CS = getChecksumAddress(TOKEN);
|
|
224
|
+
const SPENDER =
|
|
225
|
+
"0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";
|
|
226
|
+
|
|
182
227
|
test("creates ApprovalPolicy for approve methods with spender and amount", () => {
|
|
183
228
|
const policies: ParsedSessionPolicies = {
|
|
184
229
|
verified: false,
|
|
185
230
|
contracts: {
|
|
186
|
-
|
|
231
|
+
[TOKEN]: {
|
|
187
232
|
methods: [
|
|
188
233
|
{
|
|
189
234
|
entrypoint: "approve",
|
|
190
|
-
spender:
|
|
235
|
+
spender: SPENDER,
|
|
191
236
|
amount: "1000000000000000000",
|
|
192
237
|
authorized: true,
|
|
193
238
|
},
|
|
@@ -200,8 +245,8 @@ describe("toWasmPolicies", () => {
|
|
|
200
245
|
|
|
201
246
|
expect(result).toHaveLength(1);
|
|
202
247
|
expect(result[0]).toEqual({
|
|
203
|
-
target:
|
|
204
|
-
spender:
|
|
248
|
+
target: TOKEN_CS,
|
|
249
|
+
spender: SPENDER,
|
|
205
250
|
amount: "1000000000000000000",
|
|
206
251
|
});
|
|
207
252
|
// Should NOT have method or authorized fields
|
|
@@ -213,11 +258,11 @@ describe("toWasmPolicies", () => {
|
|
|
213
258
|
const policies: ParsedSessionPolicies = {
|
|
214
259
|
verified: false,
|
|
215
260
|
contracts: {
|
|
216
|
-
|
|
261
|
+
[TOKEN]: {
|
|
217
262
|
methods: [
|
|
218
263
|
{
|
|
219
264
|
entrypoint: "approve",
|
|
220
|
-
spender:
|
|
265
|
+
spender: SPENDER,
|
|
221
266
|
amount: 1000000000000000000,
|
|
222
267
|
authorized: true,
|
|
223
268
|
},
|
|
@@ -237,7 +282,7 @@ describe("toWasmPolicies", () => {
|
|
|
237
282
|
const policies: ParsedSessionPolicies = {
|
|
238
283
|
verified: false,
|
|
239
284
|
contracts: {
|
|
240
|
-
|
|
285
|
+
[TOKEN]: {
|
|
241
286
|
methods: [
|
|
242
287
|
{
|
|
243
288
|
entrypoint: "approve",
|
|
@@ -267,11 +312,11 @@ describe("toWasmPolicies", () => {
|
|
|
267
312
|
const policies: ParsedSessionPolicies = {
|
|
268
313
|
verified: false,
|
|
269
314
|
contracts: {
|
|
270
|
-
|
|
315
|
+
[TOKEN]: {
|
|
271
316
|
methods: [
|
|
272
317
|
{
|
|
273
318
|
entrypoint: "approve",
|
|
274
|
-
spender:
|
|
319
|
+
spender: SPENDER,
|
|
275
320
|
authorized: true,
|
|
276
321
|
},
|
|
277
322
|
],
|
|
@@ -290,10 +335,14 @@ describe("toWasmPolicies", () => {
|
|
|
290
335
|
});
|
|
291
336
|
|
|
292
337
|
test("creates CallPolicy for non-approve methods", () => {
|
|
338
|
+
const CONTRACT =
|
|
339
|
+
"0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49";
|
|
340
|
+
const CONTRACT_CS = getChecksumAddress(CONTRACT);
|
|
341
|
+
|
|
293
342
|
const policies: ParsedSessionPolicies = {
|
|
294
343
|
verified: false,
|
|
295
344
|
contracts: {
|
|
296
|
-
|
|
345
|
+
[CONTRACT]: {
|
|
297
346
|
methods: [
|
|
298
347
|
{
|
|
299
348
|
entrypoint: "transfer",
|
|
@@ -307,7 +356,7 @@ describe("toWasmPolicies", () => {
|
|
|
307
356
|
const result = toWasmPolicies(policies);
|
|
308
357
|
|
|
309
358
|
expect(result).toHaveLength(1);
|
|
310
|
-
expect(result[0]).toHaveProperty("target",
|
|
359
|
+
expect(result[0]).toHaveProperty("target", CONTRACT_CS);
|
|
311
360
|
expect(result[0]).toHaveProperty("method");
|
|
312
361
|
expect(result[0]).toHaveProperty("authorized", true);
|
|
313
362
|
});
|
|
@@ -316,11 +365,11 @@ describe("toWasmPolicies", () => {
|
|
|
316
365
|
const policies: ParsedSessionPolicies = {
|
|
317
366
|
verified: false,
|
|
318
367
|
contracts: {
|
|
319
|
-
|
|
368
|
+
[TOKEN]: {
|
|
320
369
|
methods: [
|
|
321
370
|
{
|
|
322
371
|
entrypoint: "approve",
|
|
323
|
-
spender:
|
|
372
|
+
spender: SPENDER,
|
|
324
373
|
amount: "1000",
|
|
325
374
|
authorized: true,
|
|
326
375
|
},
|
|
@@ -339,7 +388,7 @@ describe("toWasmPolicies", () => {
|
|
|
339
388
|
|
|
340
389
|
// First should be approve (sorted alphabetically)
|
|
341
390
|
const approvePolicy = result[0];
|
|
342
|
-
expect(approvePolicy).toHaveProperty("spender",
|
|
391
|
+
expect(approvePolicy).toHaveProperty("spender", SPENDER);
|
|
343
392
|
expect(approvePolicy).toHaveProperty("amount", "1000");
|
|
344
393
|
|
|
345
394
|
// Second should be transfer
|
|
@@ -352,12 +401,12 @@ describe("toWasmPolicies", () => {
|
|
|
352
401
|
const policies: ParsedSessionPolicies = {
|
|
353
402
|
verified: false,
|
|
354
403
|
contracts: {
|
|
355
|
-
|
|
404
|
+
[TOKEN]: {
|
|
356
405
|
methods: [
|
|
357
406
|
{ entrypoint: "transfer", authorized: true },
|
|
358
407
|
{
|
|
359
408
|
entrypoint: "approve",
|
|
360
|
-
spender:
|
|
409
|
+
spender: SPENDER,
|
|
361
410
|
amount: "1000",
|
|
362
411
|
authorized: true,
|
|
363
412
|
},
|
package/src/controller.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { KEYCHAIN_URL } from "./constants";
|
|
|
15
15
|
import { HeadlessAuthenticationError, NotReadyToConnect } from "./errors";
|
|
16
16
|
import { KeychainIFrame } from "./iframe";
|
|
17
17
|
import BaseProvider from "./provider";
|
|
18
|
+
import { lookupUsername as lookupUsernameApi } from "./lookup";
|
|
18
19
|
import {
|
|
19
20
|
AuthOptions,
|
|
20
21
|
Chain,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
ProfileContextTypeVariant,
|
|
29
30
|
ResponseCodes,
|
|
30
31
|
OpenOptions,
|
|
32
|
+
HeadlessUsernameLookupResult,
|
|
31
33
|
StarterpackOptions,
|
|
32
34
|
} from "./types";
|
|
33
35
|
import { validateRedirectUrl } from "./url-validator";
|
|
@@ -530,6 +532,17 @@ export default class ControllerProvider extends BaseProvider {
|
|
|
530
532
|
return this.keychain.username();
|
|
531
533
|
}
|
|
532
534
|
|
|
535
|
+
async lookupUsername(
|
|
536
|
+
username: string,
|
|
537
|
+
): Promise<HeadlessUsernameLookupResult> {
|
|
538
|
+
const trimmed = username.trim();
|
|
539
|
+
if (!trimmed) {
|
|
540
|
+
throw new Error("Username is required");
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return lookupUsernameApi(trimmed, this.selectedChain);
|
|
544
|
+
}
|
|
545
|
+
|
|
533
546
|
openPurchaseCredits() {
|
|
534
547
|
if (!this.iframes) {
|
|
535
548
|
return;
|
package/src/iframe/keychain.ts
CHANGED
|
@@ -104,6 +104,13 @@ export class KeychainIFrame extends IFrame<Keychain> {
|
|
|
104
104
|
);
|
|
105
105
|
} else if (preset) {
|
|
106
106
|
_url.searchParams.set("preset", preset);
|
|
107
|
+
if (policies) {
|
|
108
|
+
console.warn(
|
|
109
|
+
"[Controller] Both `preset` and `policies` provided to ControllerProvider. " +
|
|
110
|
+
"Policies are ignored when preset is set. " +
|
|
111
|
+
"Use `shouldOverridePresetPolicies: true` to override.",
|
|
112
|
+
);
|
|
113
|
+
}
|
|
107
114
|
}
|
|
108
115
|
|
|
109
116
|
// Add encrypted blob to URL fragment (hash) if present
|