@moonpay/cli 1.47.0 → 1.49.0
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/dist/{chunk-4EYMF7AI.js → chunk-BJHXMMTQ.js} +1 -1
- package/dist/{chunk-753FBGTU.js → chunk-DN5ZYCYU.js} +1 -1
- package/dist/{chunk-2X5HE7GZ.js → chunk-OWDIPU5H.js} +3 -3
- package/dist/{chunk-XMUFLWN7.js → chunk-R3CP2PEY.js} +2 -2
- package/dist/{client-UE7LZP4Q.js → client-TQF37DOB.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/{ledger-LY4YSOSC.js → ledger-H7DQENTB.js} +2 -2
- package/dist/{mcp-D3PW7HPS.js → mcp-LZ6YWF6J.js} +1 -1
- package/dist/{store-KNRHJ476.js → store-NHRSM5UA.js} +1 -1
- package/package.json +1 -1
- package/skills/moonpay-card-checkout/SKILL.md +223 -0
- package/skills/moonpay-polymarket/SKILL.md +2 -0
- package/skills/moonpay-polymarket-feeds/SKILL.md +100 -0
- package/skills/moonpay-polymarket-strategy/SKILL.md +115 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
process.noDeprecation = true; import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);
|
|
2
|
-
import{a as V,b as T,c as G,d as L,h as D,p as Z}from"./chunk-
|
|
2
|
+
import{a as V,b as T,c as G,d as L,h as D,p as Z}from"./chunk-BJHXMMTQ.js";import{a as q,f as I}from"./chunk-LMDE72OE.js";import{existsSync as H,readFileSync as N,writeFileSync as he,renameSync as J}from"fs";import{join as S}from"path";import{homedir as ge}from"os";import{randomBytes as we}from"crypto";import*as i from"@open-wallet-standard/core";import Y from"bs58";T();import{z as s}from"zod";var M={"eip155:1":"ethereum","solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp":"solana","bip122:000000000019d6689c085ae165831e93":"bitcoin","tron:mainnet":"tron","ton:mainnet":"ton","fil:mainnet":"filecoin"},E=s.object({solana:s.string().optional(),ethereum:s.string().optional(),bitcoin:s.string().optional(),tron:s.string().optional(),ton:s.string().optional(),filecoin:s.string().optional()}),Q=["base","arbitrum","polygon","optimism","bnb","bnb-testnet","avalanche","tempo","tempo-moderato","ethereum-sepolia","base-sepolia","arbitrum-sepolia","polygon-amoy"];function Ie(e){let t={};if(e.solana&&(t.solana=e.solana,t["solana-devnet"]=e.solana),e.ethereum){t.ethereum=e.ethereum;for(let r of Q)t[r]=e.ethereum}return e.bitcoin&&(t.bitcoin=e.bitcoin),e.tron&&(t.tron=e.tron),e.ton&&(t.ton=e.ton),e.filecoin&&(t.filecoin=e.filecoin),t}var X=s.object({name:s.string(),type:s.literal("hd"),mnemonic:s.string().optional(),addresses:E,createdAt:s.string()}),ee=s.object({name:s.string(),type:s.literal("imported"),chain:s.string().optional(),privateKey:s.string().optional(),addresses:E,createdAt:s.string()}),R=s.enum(["ledger"]),$=s.object({name:s.string(),type:s.literal("hardware"),device:R,addresses:E,createdAt:s.string()}),Te=s.discriminatedUnion("type",[X,ee,$]),De=s.object({name:s.string(),type:s.enum(["hd","imported","hardware"]),device:R.optional(),addresses:s.record(s.string(),s.string()),createdAt:s.string()});import{createHash as U}from"crypto";import{HDKey as x}from"@scure/bip32";import{mnemonicToSeedSync as y}from"@scure/bip39";import{keccak_256 as F}from"@noble/hashes/sha3";import{derivePath as _}from"ed25519-hd-key";import*as j from"bitcoinjs-lib";import te from"ecpair";import*as K from"@bitcoinerlab/secp256k1";import{Keypair as re}from"@solana/web3.js";import ne from"bs58";import{WalletContractV5R1 as oe}from"@ton/ton";import{keyPairFromSeed as ie}from"@ton/crypto";import*as O from"@open-wallet-standard/core";var se=te(K);function z(e,t,r=0){switch(t){case"solana":return ae(e,r);case"ethereum":return le(e,r);case"bitcoin":return fe(e,r);case"tron":return pe(e,r);case"ton":return me(e,r);case"filecoin":return ye(e,r);default:throw new Error(`Unsupported key family for derivation: ${t}`)}}function ae(e,t){let r=y(e),n=`m/44'/501'/${t}'/0'`,{key:o}=_(n,Buffer.from(r).toString("hex")),a=re.fromSeed(Uint8Array.from(o));return{privateKey:a.secretKey,address:a.publicKey.toBase58()}}function le(e,t){let r=y(e),n=x.fromMasterSeed(r),o=`m/44'/60'/${t}'/0/0`,a=n.derive(o);if(!a.privateKey)throw new Error("Failed to derive EVM private key");let l=ce(a.publicKey);return{privateKey:a.privateKey,address:l}}function ce(e){let t=K.pointCompress(e,!1),n=F(t.slice(1)).slice(-20),o="0x"+Buffer.from(n).toString("hex");return de(o)}function de(e){let t=e.toLowerCase().replace("0x",""),r=Buffer.from(F(Buffer.from(t,"utf8"))).toString("hex"),n="0x";for(let o=0;o<t.length;o++)n+=parseInt(r[o],16)>=8?t[o].toUpperCase():t[o];return n}function fe(e,t){let r=y(e),n=x.fromMasterSeed(r),o=`m/84'/0'/${t}'/0/0`,a=n.derive(o);if(!a.privateKey)throw new Error("Failed to derive Bitcoin private key");let l=se.fromPrivateKey(Buffer.from(a.privateKey)),{address:f}=j.payments.p2wpkh({pubkey:Buffer.from(l.publicKey)});if(!f)throw new Error("Failed to derive Bitcoin address");return{privateKey:a.privateKey,address:f}}function pe(e,t){let r=y(e),n=x.fromMasterSeed(r),o=`m/44'/195'/${t}'/0/0`,a=n.derive(o);if(!a.privateKey)throw new Error("Failed to derive Tron private key");let l=ue(a.publicKey);return{privateKey:a.privateKey,address:l}}function ue(e){let t=K.pointCompress(e,!1),n=F(t.slice(1)).slice(-20),o=Buffer.concat([Buffer.from([65]),Buffer.from(n)]),a=U("sha256").update(o).digest(),l=U("sha256").update(a).digest(),f=Buffer.concat([o,l.slice(0,4)]);return ne.encode(f)}function me(e,t){let r=y(e),n=`m/44'/607'/${t}'/0'`,{key:o}=_(n,Buffer.from(r).toString("hex")),a=ie(Buffer.from(o)),l=oe.create({publicKey:Buffer.from(a.publicKey)});return{privateKey:a.secretKey,address:l.address.toString({bounceable:!1})}}function ye(e,t){let r=y(e),n=x.fromMasterSeed(r),o=`m/44'/461'/${t}'/0/0`,a=n.derive(o);if(!a.privateKey)throw new Error("Failed to derive Filecoin private key");let l=O.deriveAddress(e,"filecoin",t);return{privateKey:a.privateKey,address:l}}Z();var b=S(ge(),".config","moonpay"),B=S(b,"hardware-wallets.json");function c(){return b}function ve(e){let t={};for(let r of e){let n=M[r.chainId];n&&(t[n]=r.address)}return t}function h(e){let t=ve(e.accounts);if(!t.filecoin)try{let r=i.exportWallet(e.id,null,c());r&&!r.startsWith("{")&&(t.filecoin=i.deriveAddress(r,"filecoin"))}catch{}return{name:e.name,type:"hd",addresses:t,createdAt:e.createdAt}}function m(){if(!H(B))return[];try{return(JSON.parse(N(B,"utf-8")).wallets??[]).map(t=>$.parse(t))}catch{return[]}}function We(e,t){D();let r=S(b,`.tmp.${we(4).toString("hex")}`);he(r,JSON.stringify(t,null,2),{mode:384}),J(r,e)}function g(e){We(B,{wallets:e})}var w=S(b,"wallets.json");function P(){if(H(w)){process.stderr.write(`Migrating legacy wallets...
|
|
3
3
|
`);let{migrated:e,skipped:t}=xe();process.stderr.write(`Migrated ${e} wallet(s), skipped ${t}.
|
|
4
|
-
`)}}function xe(){if(!H(w))throw new Error("No legacy wallets.json found \u2014 nothing to migrate.");let{getEncryptionKey:e}=(L(),I(G)),{decrypt:t,encryptedFileSchema:r}=(T(),I(V)),n=e();if(!n)throw new Error("Encryption key not found. Set MOONPAY_ENCRYPTION_KEY or ensure OS keychain is accessible.");let o=JSON.parse(N(w,"utf-8")),a=r.parse(o),l=t(a,n),f=JSON.parse(l).wallets??[],p=c(),W=m(),C=0,k=0;for(let d of f){try{i.getWallet(d.name,p),k++;continue}catch{}if(W.some(u=>u.name===d.name)){k++;continue}if(d.type==="hardware")W.push({name:d.name,type:"hardware",device:d.device,addresses:d.addresses,createdAt:d.createdAt});else if(d.type==="hd"&&d.mnemonic)i.importWalletMnemonic(d.name,d.mnemonic,null,null,p);else if(d.type==="imported"&&d.privateKey){let u=d.privateKey;if(!/^[0-9a-fA-F]+$/.test(u)){let A=Y.decode(u);u=Buffer.from(A.length===64?A.slice(0,32):A).toString("hex")}i.importWalletPrivateKey(d.name,u,null,p,null,u,u)}C++}return W.length>0&&g(W),J(w,w+".migrated"),{migrated:C,skipped:k}}function Ke(e){P(),v(e);let t=i.createWallet(e,null,24,c());return h(t)}function Se(e,t){v(e);let r=i.importWalletMnemonic(e,t,null,null,c());return h(r)}function be(e,t){return v(e),h(i.importWalletPrivateKey(e,t,null,c()))}function ke(){P();let e=[];try{e=i.listWallets(c())}catch{}return[...e.map(r=>h(r)),...m()]}function Ae(e){P();let t=c();try{let r=i.getWallet(e,t);return h(r)}catch{}try{for(let r of i.listWallets(t))for(let n of r.accounts)if(n.address===e)return h(r)}catch{}for(let r of m()){if(r.name===e)return r;for(let n of Object.values(r.addresses))if(n===e)return r}return null}function nt(e){let t=Ae(e);if(!t)throw new Error(`Wallet "${e}" not found`);return t}function Ee(e){try{i.deleteWallet(e,c());return}catch{}let t=m(),r=t.findIndex(n=>n.name===e);if(r===-1)throw new Error(`Wallet "${e}" not found`);t.splice(r,1),g(t)}function ot(e,t){if(e===t)return;v(t);try{i.getWallet(e,c()),i.renameWallet(e,t,c());return}catch(o){if(o instanceof Error&&!o.message.includes("not found"))throw o}let r=m(),n=r.find(o=>o.name===e);if(!n)throw new Error(`Wallet "${e}" not found`);n.name=t,g(r)}function it(e){return i.exportWallet(e,null,c())}function $e(e){v(e.name);let t=m();t.push(e),g(t)}function st(e,t){let r=m(),n=r.find(o=>o.name===e);if(!n)throw new Error(`Hardware wallet "${e}" not found`);n.addresses=t,g(r)}function at(e,t,r){return i.signTransaction(e,t,r,null,null,c())}function lt(e,t,r,n){return i.signMessage(e,t,r,null,n??null,null,c())}function ct(e,t,r){return i.signTypedData(e,t,r,null,null,c())}async function dt(e,t){if(e.type==="hardware")throw new Error(`Wallet "${e.name}" is a hardware wallet. Signing must happen on the device.`);let{moonpay:r}=await import("./client-
|
|
4
|
+
`)}}function xe(){if(!H(w))throw new Error("No legacy wallets.json found \u2014 nothing to migrate.");let{getEncryptionKey:e}=(L(),I(G)),{decrypt:t,encryptedFileSchema:r}=(T(),I(V)),n=e();if(!n)throw new Error("Encryption key not found. Set MOONPAY_ENCRYPTION_KEY or ensure OS keychain is accessible.");let o=JSON.parse(N(w,"utf-8")),a=r.parse(o),l=t(a,n),f=JSON.parse(l).wallets??[],p=c(),W=m(),C=0,k=0;for(let d of f){try{i.getWallet(d.name,p),k++;continue}catch{}if(W.some(u=>u.name===d.name)){k++;continue}if(d.type==="hardware")W.push({name:d.name,type:"hardware",device:d.device,addresses:d.addresses,createdAt:d.createdAt});else if(d.type==="hd"&&d.mnemonic)i.importWalletMnemonic(d.name,d.mnemonic,null,null,p);else if(d.type==="imported"&&d.privateKey){let u=d.privateKey;if(!/^[0-9a-fA-F]+$/.test(u)){let A=Y.decode(u);u=Buffer.from(A.length===64?A.slice(0,32):A).toString("hex")}i.importWalletPrivateKey(d.name,u,null,p,null,u,u)}C++}return W.length>0&&g(W),J(w,w+".migrated"),{migrated:C,skipped:k}}function Ke(e){P(),v(e);let t=i.createWallet(e,null,24,c());return h(t)}function Se(e,t){v(e);let r=i.importWalletMnemonic(e,t,null,null,c());return h(r)}function be(e,t){return v(e),h(i.importWalletPrivateKey(e,t,null,c()))}function ke(){P();let e=[];try{e=i.listWallets(c())}catch{}return[...e.map(r=>h(r)),...m()]}function Ae(e){P();let t=c();try{let r=i.getWallet(e,t);return h(r)}catch{}try{for(let r of i.listWallets(t))for(let n of r.accounts)if(n.address===e)return h(r)}catch{}for(let r of m()){if(r.name===e)return r;for(let n of Object.values(r.addresses))if(n===e)return r}return null}function nt(e){let t=Ae(e);if(!t)throw new Error(`Wallet "${e}" not found`);return t}function Ee(e){try{i.deleteWallet(e,c());return}catch{}let t=m(),r=t.findIndex(n=>n.name===e);if(r===-1)throw new Error(`Wallet "${e}" not found`);t.splice(r,1),g(t)}function ot(e,t){if(e===t)return;v(t);try{i.getWallet(e,c()),i.renameWallet(e,t,c());return}catch(o){if(o instanceof Error&&!o.message.includes("not found"))throw o}let r=m(),n=r.find(o=>o.name===e);if(!n)throw new Error(`Wallet "${e}" not found`);n.name=t,g(r)}function it(e){return i.exportWallet(e,null,c())}function $e(e){v(e.name);let t=m();t.push(e),g(t)}function st(e,t){let r=m(),n=r.find(o=>o.name===e);if(!n)throw new Error(`Hardware wallet "${e}" not found`);n.addresses=t,g(r)}function at(e,t,r){return i.signTransaction(e,t,r,null,null,c())}function lt(e,t,r,n){return i.signMessage(e,t,r,null,n??null,null,c())}function ct(e,t,r){return i.signTypedData(e,t,r,null,null,c())}async function dt(e,t){if(e.type==="hardware")throw new Error(`Wallet "${e.name}" is a hardware wallet. Signing must happen on the device.`);let{moonpay:r}=await import("./client-TQF37DOB.js"),o=(await r.chain.retrieve({chain:t})).keyFamily,a=i.exportWallet(e.name,null,c());if(a.includes(" "))return z(a,o);let l;if(a.startsWith("{")){let p=JSON.parse(a);l=o==="solana"||o==="ton"?p.ed25519:p.secp256k1}else l=a;let f=Uint8Array.from(Buffer.from(l,"hex"));if(o==="solana"&&f.length===32){let p=q("tweetnacl");f=Uint8Array.from(p.sign.keyPair.fromSeed(f).secretKey)}return{privateKey:f,address:e.addresses[o]??""}}var Fe=ke,ft=Ee;function Be(e){if(e.type==="hardware"){$e(e);return}if(e.type==="hd"&&e.mnemonic){Se(e.name,e.mnemonic);return}if(e.type==="imported"&&e.privateKey){let t=e.privateKey;if(t.startsWith("0x"))t=t.slice(2);else if(!/^[0-9a-fA-F]+$/.test(t)){let r=Y.decode(t);t=Buffer.from(r.length===64?r.slice(0,32):r).toString("hex")}be(e.name,t);return}Ke(e.name)}function pt(e){for(let t of e)Be(t)}function ut(e){let t=Fe();e(t),g(t.filter(r=>r.type==="hardware"))}function v(e){try{throw i.getWallet(e,c()),new Error(`Wallet "${e}" already exists`)}catch(t){if(t instanceof Error&&t.message.includes("already exists"))throw t}if(m().some(t=>t.name===e))throw new Error(`Wallet "${e}" already exists`)}export{E as a,Ie as b,R as c,De as d,z as e,c as f,P as g,xe as h,Ke as i,Se as j,be as k,ke as l,Ae as m,nt as n,Ee as o,ot as p,it as q,$e as r,st as s,at as t,lt as u,ct as v,dt as w,Fe as x,ft as y,Be as z,pt as A,ut as B};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
process.noDeprecation = true; import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);
|
|
2
|
-
import{b as a,c as b}from"./chunk-
|
|
2
|
+
import{b as a,c as b}from"./chunk-DN5ZYCYU.js";import"./chunk-BJHXMMTQ.js";import"./chunk-LMDE72OE.js";export{a as moonpay,b as resetClient};
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
process.noDeprecation = true; import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);
|
|
3
|
-
import{a as J,b as K,c as R,d as C}from"./chunk-
|
|
3
|
+
import{a as J,b as K,c as R,d as C}from"./chunk-OWDIPU5H.js";import{b as N}from"./chunk-DN5ZYCYU.js";import{m as A}from"./chunk-R3CP2PEY.js";import{e as v,g as H}from"./chunk-BJHXMMTQ.js";import"./chunk-LMDE72OE.js";import{Command as se}from"commander";var W=!process.env.NO_COLOR&&process.stdout.isTTY===!0;function _(e){return n=>W?`${e}${n}\x1B[0m`:n}var d={bold:_("\x1B[1m"),dim:_("\x1B[2m"),green:_("\x1B[32m"),yellow:_("\x1B[33m"),cyan:_("\x1B[36m"),red:_("\x1B[31m")};function O(e){if(e==null)return!0;let n=typeof e;return n==="string"||n==="number"||n==="boolean"}function w(e){return e==null?"-":String(e)}function S(e,n=0){let t=" ".repeat(n);return O(e)?`${t}${w(e)}`:Array.isArray(e)?e.length===0?`${t}${d.dim("(empty)")}`:e.every(O)?e.map(s=>`${t}- ${w(s)}`).join(`
|
|
4
4
|
`):e.map(s=>{if(typeof s!="object"||s===null)return`${t}- ${w(s)}`;let r=Object.entries(s),c=[],[l,u]=r[0];O(u)?c.push(`${t}- ${d.bold(l)}: ${w(u)}`):(c.push(`${t}- ${d.bold(l)}:`),c.push(S(u,n+2)));for(let[p,g]of r.slice(1))O(g)?c.push(`${t} ${d.bold(p)}: ${w(g)}`):(c.push(`${t} ${d.bold(p)}:`),c.push(S(g,n+2)));return c.join(`
|
|
5
5
|
`)}).join(`
|
|
6
6
|
|
|
@@ -14,5 +14,5 @@ ${S(c,n+1)}`).join(`
|
|
|
14
14
|
|
|
15
15
|
Run \`mp skill install\` to install AI skills for Claude Code.
|
|
16
16
|
|
|
17
|
-
`+K).version(v).option("--json","Output as JSON instead of YAML");function ae(){return h.opts().json?"json":"yaml"}function U(e){console.log(F(e,ae()))}function M(){let e=ie();e&&process.stderr.write(e)}h.command("mcp").description("Start MCP server over stdio (for Claude Desktop, Claude Code, etc.)").action(async()=>{let{startMcpServer:e}=await import("./mcp-
|
|
17
|
+
`+K).version(v).option("--json","Output as JSON instead of YAML");function ae(){return h.opts().json?"json":"yaml"}function U(e){console.log(F(e,ae()))}function M(){let e=ie();e&&process.stderr.write(e)}h.command("mcp").description("Start MCP server over stdio (for Claude Desktop, Claude Code, etc.)").action(async()=>{let{startMcpServer:e}=await import("./mcp-LZ6YWF6J.js");await e()});function q(e,n){let t=e;for(let s of n){let r=t.commands.find(c=>c.name()===s);r||(r=t.command(s)),t=r}return t}function z(e,n=""){let t=[];for(let[s,r]of Object.entries(e)){let c=n?`${n}-${s}`:s,l=r;for(;l._def.typeName==="ZodEffects";)l=l._def.schema;let u=l;u._def.typeName==="ZodNullable"&&(u=u._def.innerType),u._def.typeName==="ZodObject"&&u.shape?t.push(...z(u.shape,c)):t.push({flatKey:c,field:r,path:c.split("-")})}return t}function ce(e,n){let t={};for(let{flatKey:s,path:r}of n){let c=s.replace(/-([a-z])/g,(u,p)=>p.toUpperCase()),l=c in e?e[c]:e[s];if(r.length===1)t[r[0]]=l;else{let u=t;for(let p=0;p<r.length-1;p++)(!u[r[p]]||typeof u[r[p]]!="object")&&(u[r[p]]={}),u=u[r[p]];u[r[r.length-1]]=l}}return t}function le(e,n){for(let t of n){let s=t.schema.name.split("_"),r=s.pop(),l=q(e,s).command(r).description(t.schema.description),u=t.schema.input.shape??{},p=z(u),g=[],b=[],k=[],$=[];for(let{flatKey:f,field:i}of p){let o=i.description??f,a=i;for(;a._def.typeName==="ZodEffects";)a=a._def.schema;let y=a._def.typeName,m=a;m._def.typeName==="ZodNullable"&&(m=m._def.innerType),m._def.typeName==="ZodNumber"&&b.push(f),m._def.typeName==="ZodRecord"&&k.push(f),m._def.typeName==="ZodBoolean"&&$.push(f),y==="ZodBoolean"?l.option(`--${f} [${f}]`,o,!1):y==="ZodNullable"?(l.option(`--${f} <${f}>`,o),g.push(f)):l.requiredOption(`--${f} <${f}>`,o)}l.action(async f=>{t.schema.name.startsWith("consent_")||await R.handler({});for(let o of g){let a=o.replace(/-([a-z])/g,(y,m)=>m.toUpperCase());f[a]===void 0&&(f[a]=null)}for(let o of b){let a=o.replace(/-([a-z])/g,(y,m)=>m.toUpperCase());f[a]!=null&&typeof f[a]=="string"&&(f[a]=Number(f[a]))}for(let o of $){let a=o.replace(/-([a-z])/g,(m,j)=>j.toUpperCase()),y=f[a];if(typeof y=="string"){let m=y.toLowerCase();m==="true"||m==="1"?f[a]=!0:(m==="false"||m==="0")&&(f[a]=!1)}}for(let o of k){let a=o.replace(/-([a-z])/g,(y,m)=>m.toUpperCase());if(typeof f[a]=="string")try{f[a]=JSON.parse(f[a])}catch{console.error(`Invalid JSON for --${o}`),process.exit(1)}}let i=ce(f,p);try{let o=await t.handler(i);U(o)}catch(o){console.error(Z(o)),process.exit(1)}})}}function fe(e){if(e.$ref&&e.definitions){let n=e.$ref.replace("#/definitions/","");return e.definitions[n]}return e}function ue(e){return Array.isArray(e.type)?e.type.includes("null"):e.anyOf?e.anyOf.some(n=>n.type==="null"):!1}function me(e){return Array.isArray(e.type)?e.type.includes("object"):e.anyOf?e.anyOf.some(n=>n.type==="object"):e.type==="object"}function pe(e){return Array.isArray(e.type)?e.type.some(n=>n==="number"||n==="integer"):e.anyOf?e.anyOf.some(n=>n.type==="number"||n.type==="integer"):e.type==="number"||e.type==="integer"}function de(e){return Array.isArray(e.type)?e.type.includes("boolean"):e.anyOf?e.anyOf.some(n=>n.type==="boolean"):e.type==="boolean"}var Y=new Set(["swaps_transaction_build","transaction_register","virtual-account_offramp_initiate","prediction-market_position_buy","prediction-market_position_sell","prediction-market_position_redeem","commerce_cart_update","commerce_checkout_start","commerce_checkout_pay","card_delegation_approve_transaction_build","card_delegation_revoke_transaction_build","card_delegation_token_retrieve","card_wallet_link","card_wallet_unlink"]);function ye(e,n){for(let t of n){if(Y.has(t.name))continue;let s=t.name.split("_"),r=s.pop(),c=q(e,s);if(c.commands.find(i=>i.name()===r))continue;let l=c.command(r).description(t.description),u=fe(t.inputSchema),p=u.properties??{},g=new Set(u.required??[]),b=[],k=[],$=[],f=[];for(let[i,o]of Object.entries(p)){let a=o.description??i,y=ue(o),m=me(o),j=pe(o),B=de(o);j&&k.push(i),B&&f.push(i),m?($.push(i),g.has(i)&&!y?l.requiredOption(`--${i} <json>`,`${a} (as JSON)`):(l.option(`--${i} <json>`,`${a} (as JSON)`),b.push(i))):y||!g.has(i)?(l.option(`--${i} <${i}>`,a),b.push(i)):l.requiredOption(`--${i} <${i}>`,a)}l.action(async i=>{await R.handler({});for(let o of b)i[o]===void 0&&(i[o]=null);for(let o of k)i[o]!=null&&typeof i[o]=="string"&&(i[o]=Number(i[o]));for(let o of f)if(typeof i[o]=="string"){let a=i[o].toLowerCase();a==="true"||a==="1"?i[o]=!0:(a==="false"||a==="0")&&(i[o]=!1)}for(let o of $)if(typeof i[o]=="string")try{i[o]=JSON.parse(i[o])}catch{console.error(`Invalid JSON for --${o}`),process.exit(1)}try{await I(i);let o=await N._call(t.name,i);U(o),M()}catch(o){console.error(Z(o)),process.exit(1)}})}}le(h,C);ye(h,J);h.command("tools").description("List available tools").action(()=>{let e=[...C.map(t=>({name:t.schema.name,description:t.schema.description})),...J.filter(t=>!C.some(s=>s.schema.name===t.name)&&!Y.has(t.name)).map(t=>({name:t.name,description:t.description}))].sort((t,s)=>t.name.localeCompare(s.name)),n=[];for(let t of e){let s=t.name.replace(/_/g," ");n.push(` ${d.green(s.padEnd(28))} ${d.dim(t.description)}`)}n.push(""),n.push(d.dim(`${e.length} tools`)),console.log(n.join(`
|
|
18
18
|
`)),M()});h.parse();
|
|
@@ -7,6 +7,6 @@ Run: npm i -g @moonpay/cli --include=optional`;function y(){try{let e=S("@ledger
|
|
|
7
7
|
`),p}async function U(){if(!p)return null;try{let e=y(),i=f().getDeviceSessionState({sessionId:p}),s=await e.firstValueFrom(i.pipe(e.take(1),e.timeout(3e3)));return s.sessionStateType>=1?s.currentApp?.name??null:null}catch{return null}}var M={evm:"Ethereum",solana:"Solana"};function _(e){let n=String(e);return n.includes("6807")||n.includes("6a15")||n.includes("UnknownDeviceExchangeError")}async function C(e){process.stderr.write(`Installing ${e} app on your Ledger...
|
|
8
8
|
`);let{OpenAppWithDependenciesDeviceAction:n}=y(),i=new n({input:{appName:e}}),t=f().executeDeviceAction({sessionId:E(),deviceAction:i});await m(t,`Install ${e}`,18e4),process.stderr.write(`Installed ${e} app
|
|
9
9
|
`)}async function R(e){let n=M[e];if(!n||await U()===n)return;process.stderr.write(`Opening ${n} app on your Ledger...
|
|
10
|
-
`);let{OpenAppDeviceAction:s}=y(),t=new s({input:{appName:n}}),u=f().executeDeviceAction({sessionId:E(),deviceAction:t});try{await m(u,`Open ${n}`)}catch(c){if(_(c)){await D(),await C(n),await D();return}if(!String(c).includes("isconnect"))throw c}await D()}async function D(){try{await f().disconnect({sessionId:p})}catch{}p=null,await new Promise(e=>setTimeout(e,500)),await P()}function $(e,n=0){switch(e){case"evm":return`44'/60'/${n}'/0/0`;case"solana":return`44'/501'/${n}'/0'`;default:throw new Error(`${e} derivation via Ledger is not supported.`)}}function k(e){return e.startsWith("0x")?e.slice(2):e}function T(e){return e.startsWith("0x")?e:`0x${e}`}async function x(e){let{moonpay:n}=await import("./client-
|
|
10
|
+
`);let{OpenAppDeviceAction:s}=y(),t=new s({input:{appName:n}}),u=f().executeDeviceAction({sessionId:E(),deviceAction:t});try{await m(u,`Open ${n}`)}catch(c){if(_(c)){await D(),await C(n),await D();return}if(!String(c).includes("isconnect"))throw c}await D()}async function D(){try{await f().disconnect({sessionId:p})}catch{}p=null,await new Promise(e=>setTimeout(e,500)),await P()}function $(e,n=0){switch(e){case"evm":return`44'/60'/${n}'/0/0`;case"solana":return`44'/501'/${n}'/0'`;default:throw new Error(`${e} derivation via Ledger is not supported.`)}}function k(e){return e.startsWith("0x")?e.slice(2):e}function T(e){return e.startsWith("0x")?e:`0x${e}`}async function x(e){let{moonpay:n}=await import("./client-TQF37DOB.js"),s=(await n.chain.retrieve({chain:e})).keyFamily;if(s==="bitcoin")throw new Error("Bitcoin address derivation via Ledger is not yet supported.");if(s==="tron")throw new Error("Tron address derivation via Ledger is not supported. The Ethereum signer cannot interact with the Tron Ledger app.");await R(s);let t=$(s),a=E(),u=f();switch(s){case"evm":{let{SignerEthBuilder:c}=I(),l=new c({dmk:u,sessionId:a}).build().getAddress(t,{skipOpenApp:!1});return(await m(l,"Get Ethereum address")).address}case"solana":{let{SignerSolanaBuilder:c}=L(),l=new c({dmk:u,sessionId:a}).build().getAddress(t,{skipOpenApp:!1});return await m(l,"Get Solana address")}default:throw new Error(`Ledger address derivation not supported for ${s}`)}}async function F(){let e={};try{e.ethereum=await x("ethereum")}catch(n){process.stderr.write(`Failed to get Ethereum address: ${n}
|
|
11
11
|
`)}try{e.solana=await x("solana")}catch(n){process.stderr.write(`Failed to get Solana address: ${n}
|
|
12
|
-
`)}if(!e.ethereum&&!e.solana)throw new Error("Could not derive any addresses from Ledger device");return e}async function H(e,n){let{moonpay:i}=await import("./client-
|
|
12
|
+
`)}if(!e.ethereum&&!e.solana)throw new Error("Could not derive any addresses from Ledger device");return e}async function H(e,n){let{moonpay:i}=await import("./client-TQF37DOB.js"),t=(await i.chain.retrieve({chain:e})).keyFamily;if(t==="bitcoin")throw new Error("Bitcoin transaction signing via Ledger is not yet supported. Use a software wallet for Bitcoin transactions.");if(t==="tron")throw new Error("Tron transaction signing via Ledger is not supported. Use a software wallet for Tron transactions.");await R(t);let a=$(t),u=E(),c=f();switch(t){case"evm":{let{SignerEthBuilder:d}=I(),l=new d({dmk:c,sessionId:u}).build(),w=T(n.startsWith("0x")?n:Buffer.from(n,"base64").toString("hex")),o=Uint8Array.from(Buffer.from(k(w),"hex")),r=l.signTransaction(a,o,{skipOpenApp:!1}),g=await m(r,"Sign transaction"),{parseTransaction:v,serializeTransaction:A}=await import("viem"),B=v(w);return{transaction:A(B,{r:T(k(g.r)),s:T(k(g.s)),v:BigInt(g.v)})}}case"solana":{let{SignerSolanaBuilder:d}=L(),l=new d({dmk:c,sessionId:u}).build(),{VersionedTransaction:w}=await import("@solana/web3.js"),o=Uint8Array.from(Buffer.from(n,"base64")),r=w.deserialize(o),g=r.message.serialize(),v=l.signTransaction(a,g,{skipOpenApp:!1}),A=await m(v,"Sign Solana transaction");return r.signatures[0]=A,{transaction:Buffer.from(r.serialize()).toString("base64")}}default:throw new Error(`Ledger transaction signing not supported for ${t}`)}}async function V(e,n){let{moonpay:i}=await import("./client-TQF37DOB.js"),t=(await i.chain.retrieve({chain:e})).keyFamily;if(t==="bitcoin")throw new Error("Bitcoin message signing via Ledger is not yet supported. Use a software wallet for Bitcoin message signing.");if(t==="tron")throw new Error("Tron message signing via Ledger is not supported. Use a software wallet for Tron message signing.");await R(t);let a=$(t),u=E(),c=f();switch(t){case"evm":{let{SignerEthBuilder:d}=I(),w=new d({dmk:c,sessionId:u}).build().signMessage(a,n,{skipOpenApp:!1}),o=await m(w,"Sign message"),r=k(o.r),g=k(o.s),v=o.v.toString(16).padStart(2,"0");return{signature:`0x${r}${g}${v}`}}case"solana":{let{SignerSolanaBuilder:d}=L(),l=new d({dmk:c,sessionId:u}).build(),w=Buffer.from(n,"utf8").toString("hex"),o=l.signMessage(a,w,{skipOpenApp:!1}),r=await m(o,"Sign Solana message");return{signature:(await import("bs58")).default.encode(r)}}default:throw new Error(`Ledger message signing not supported for ${t}`)}}async function q(){if(p){try{await f().disconnect({sessionId:p})}catch{}p=null}if(h){try{h.close()}catch{}h=null}}export{P as connectLedger,q as disconnectLedger,x as ledgerGetAddress,F as ledgerGetAllAddresses,V as ledgerSignMessage,H as ledgerSignTransaction};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
process.noDeprecation = true; import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);
|
|
2
|
-
import{a as f,d as u}from"./chunk-
|
|
2
|
+
import{a as f,d as u}from"./chunk-OWDIPU5H.js";import{b as d}from"./chunk-DN5ZYCYU.js";import"./chunk-R3CP2PEY.js";import{e as l,g as R}from"./chunk-BJHXMMTQ.js";import"./chunk-LMDE72OE.js";import{Server as N}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as O}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as T,ListToolsRequestSchema as x}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as k}from"zod-to-json-schema";R();var y=new Map(u.map(s=>[s.schema.name,s]));function q(s){let n=s.inputSchema;if(n.$ref&&n.definitions){let c=n.$ref.replace("#/definitions/","");return n.definitions[c]??n}return n}async function P(){let s=new N({name:"moonpay",version:l},{capabilities:{tools:{listChanged:!0}}}),n=u.map(e=>({name:e.schema.name,description:e.schema.description,inputSchema:k(e.schema.input)})),c=new Map;for(let e of f){if(y.has(e.name))continue;let t=q(e);c.set(e.name,t.properties??{}),n.push({name:e.name,description:e.description,inputSchema:{type:"object",properties:t.properties??{},...t.required?{required:t.required}:{},...t.additionalProperties!==void 0?{additionalProperties:t.additionalProperties}:{}}})}let h=new Map(n.map(e=>[e.name,e]));s.setRequestHandler(x,async()=>({tools:n.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}))})),s.setRequestHandler(T,async e=>{let{name:t,arguments:o={}}=e.params;if(!h.has(t))return{content:[{type:"text",text:`Unknown tool: ${t}`}],isError:!0};try{let i=y.get(t);if(i){let p=i.schema.input.shape??{};for(let[m,a]of Object.entries(p))o[m]===void 0&&a._def.typeName==="ZodNullable"&&(o[m]=null);let r=await i.handler(o);return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}let S=c.get(t)??{};for(let[p,r]of Object.entries(S))o[p]===void 0&&(Array.isArray(r.type)?r.type.includes("null"):r.anyOf?.some(w=>w.type==="null"))&&(o[p]=null),(r.type==="number"||r.type==="integer"||Array.isArray(r.type)&&r.type.some(a=>a==="number"||a==="integer")||r.anyOf?.some(a=>a.type==="number"||a.type==="integer"))&&typeof o[p]=="string"&&(o[p]=Number(o[p]));let b=await d._call(t,o);return{content:[{type:"text",text:JSON.stringify(b,null,2)}]}}catch(i){return{content:[{type:"text",text:i instanceof Error?i.message:String(i)}],isError:!0}}});let g=new O;await s.connect(g)}export{P as startMcpServer};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
process.noDeprecation = true; import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);
|
|
2
|
-
import{A as v,B as w,f as a,g as b,h as c,i as d,j as e,k as f,l as g,m as h,n as i,o as j,p as k,q as l,r as m,s as n,t as o,u as p,v as q,w as r,x as s,y as t,z as u}from"./chunk-
|
|
2
|
+
import{A as v,B as w,f as a,g as b,h as c,i as d,j as e,k as f,l as g,m as h,n as i,o as j,p as k,q as l,r as m,s as n,t as o,u as p,v as q,w as r,x as s,y as t,z as u}from"./chunk-R3CP2PEY.js";import"./chunk-BJHXMMTQ.js";import"./chunk-LMDE72OE.js";export{m as addHardwareWallet,u as addWallet,d as createWallet,j as deleteWallet,l as exportSecret,h as findWallet,i as findWalletOrThrow,a as getVaultPath,e as importMnemonic,f as importPrivateKey,g as listWallets,s as loadWallets,c as migrateWallets,w as mutateWallets,p as owsSignMessage,o as owsSignTransaction,q as owsSignTypedData,n as refreshHardwareAddresses,t as removeWallet,k as renameWallet,b as requireMigrationCheck,r as resolveSigningKey,v as saveWallets};
|
package/package.json
CHANGED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: moonpay-card-checkout
|
|
3
|
+
description: Buy products from any Shopify store using the user's MoonAgents Card. Search the Shopify catalog (or accept a checkout URL), drive Playwright through shipping + card entry, and stop at the review screen for explicit user approval before clicking Pay. Pairs with the moon-agents-card Artifact for visualization.
|
|
4
|
+
tags: [card, shopping, checkout, demo]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# MoonAgents Card Checkout
|
|
8
|
+
|
|
9
|
+
## Goal
|
|
10
|
+
|
|
11
|
+
Buy a product from a Shopify store with the user's MoonAgents Card. Either:
|
|
12
|
+
- The user gives you a Shopify checkout URL → open it directly
|
|
13
|
+
- The user describes a product → search Shopify with `mp commerce search`, pick the best match, open its `checkoutUrl`
|
|
14
|
+
|
|
15
|
+
Then drive Playwright through shipping + card details, **show the total**, and only place the order with explicit user confirmation. The matching transaction lands on-chain (Solana USDC) via Baanx's spender contract — the user can watch the funding wallet's allowance tick down live in the **moon-agents-card** Artifact.
|
|
16
|
+
|
|
17
|
+
## Prerequisites (one-time, usually already done)
|
|
18
|
+
|
|
19
|
+
The MoonAgents Card runs on Monavate × Baanx. Before checkout works, the user must have:
|
|
20
|
+
- Completed Baanx KYC and received a virtual card (`mp card create`)
|
|
21
|
+
- Delegated USDC spending from a Solana wallet to the Baanx spender (`mp card delegate`)
|
|
22
|
+
|
|
23
|
+
If `mp card retrieve` errors with "No MoonCard account", point the user at `mp card onboarding start` first — don't try to checkout until the card exists.
|
|
24
|
+
|
|
25
|
+
## Setup: Playwright CLI + Chromium
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
which playwright-cli || npm install -g @playwright/cli@latest
|
|
29
|
+
playwright-cli install chromium 2>/dev/null || npx playwright install chromium
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Flow
|
|
33
|
+
|
|
34
|
+
### 1. Confirm shipping details upfront
|
|
35
|
+
|
|
36
|
+
Ask the user for everything you'll need before opening the browser:
|
|
37
|
+
- Full name (first + last)
|
|
38
|
+
- Email + phone
|
|
39
|
+
- Street address (with apt/unit)
|
|
40
|
+
- City, state, ZIP
|
|
41
|
+
- Country (defaults to US)
|
|
42
|
+
- Delivery notes if any
|
|
43
|
+
|
|
44
|
+
Don't proceed without all required fields. Filling-as-you-go inside Playwright is fragile.
|
|
45
|
+
|
|
46
|
+
### 2. Reveal the card
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
mp card retrieve --json # confirm card is active, grab last 4
|
|
50
|
+
mp card reveal # opens a PCI-safe URL with PAN/CVV/expiry as an image
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Read the PAN, expiry (convert `YYYYMM` → `MM/YY`), CVV, and name-on-card from the reveal image. **Never log these to stdout** — keep them local to the agent.
|
|
54
|
+
|
|
55
|
+
### 3. Find the product
|
|
56
|
+
|
|
57
|
+
**If user gave a checkout URL:** skip to step 4 with that URL.
|
|
58
|
+
|
|
59
|
+
**If user described a product:** search Shopify:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
mp commerce search --query "<user's description>" --shipsTo US --limit 5 --json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Returns `products[]`, each with `title`, `price`, `shop`, and `checkoutUrl`. Pick the top match (or ask the user to choose if it's ambiguous). Surface the title + price + shop so the user knows what's about to happen.
|
|
66
|
+
|
|
67
|
+
### 4. Open the checkout in Playwright
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
playwright-cli open "<checkoutUrl>" --headed
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `checkoutUrl` is a Shopify cart deep link that drops the user straight into checkout with the product pre-added.
|
|
74
|
+
|
|
75
|
+
### 5. Snapshot, find refs
|
|
76
|
+
|
|
77
|
+
Always snapshot before interacting. Never use quoted aria selectors — use element refs only.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
playwright-cli snapshot
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Refs look like `e65`, `e96`. Card fields inside iframes have refs like `f6e5`, `f7e13`. Grep the snapshot for what you need:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
grep -i -E 'textbox|combobox|email|first|last|address|city|zip|phone' snapshot.yml
|
|
87
|
+
grep -i -E 'card number|name on|expir|secur|textbox.*f[0-9]' snapshot.yml
|
|
88
|
+
grep -i -E 'button|checkbox|billing|pay now|delivery|pickup' snapshot.yml
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 6. Fill shipping
|
|
92
|
+
|
|
93
|
+
Use the right action per field type — `fill` for text, `select` for `<select>` dropdowns, `check` for checkboxes, `click` for buttons:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
playwright-cli fill <email_ref> "user@email.com"
|
|
97
|
+
playwright-cli fill <first_name_ref> "Kevin"
|
|
98
|
+
playwright-cli fill <last_name_ref> "Arifin"
|
|
99
|
+
playwright-cli fill <address_ref> "1049 Carolina St"
|
|
100
|
+
playwright-cli fill <apt_ref> "Apt 2"
|
|
101
|
+
playwright-cli fill <city_ref> "San Francisco"
|
|
102
|
+
playwright-cli select <state_ref> "California" # select, NOT fill
|
|
103
|
+
playwright-cli fill <zip_ref> "94107"
|
|
104
|
+
playwright-cli fill <phone_ref> "4085551234"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 7. Pick delivery method
|
|
108
|
+
|
|
109
|
+
Many Shopify checkouts offer delivery + in-store-pickup. Click the right one and let the shipping option auto-select. If multiple shipping rates appear, pick the cheapest unless the user said otherwise.
|
|
110
|
+
|
|
111
|
+
### 8. Fill card details
|
|
112
|
+
|
|
113
|
+
Card fields live in iframes. **Use `type` for the card number** — `fill` gets cleared by Shopify's input masking.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
playwright-cli click <card_number_ref>
|
|
117
|
+
playwright-cli type "<pan>" # type, NOT fill
|
|
118
|
+
|
|
119
|
+
playwright-cli fill <name_on_card_ref> "<name>"
|
|
120
|
+
playwright-cli fill <expiry_ref> "<mm_yy>"
|
|
121
|
+
playwright-cli fill <cvv_ref> "<cvv>"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 9. Check the billing-address checkbox
|
|
125
|
+
|
|
126
|
+
Shopify's "Use shipping address as billing address" checkbox is often **unchecked by default**. Always check it:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
playwright-cli check <billing_checkbox_ref>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
If no checkbox exists, the store has a separate billing form — fill it with the same shipping details.
|
|
133
|
+
|
|
134
|
+
### 10. Verify before paying
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
playwright-cli snapshot
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Check:
|
|
141
|
+
- All shipping fields filled
|
|
142
|
+
- Card fields show values (PAN may be masked in the snapshot — that's fine)
|
|
143
|
+
- Billing-address checkbox is checked
|
|
144
|
+
- Order total visible
|
|
145
|
+
|
|
146
|
+
**Surface to the user:**
|
|
147
|
+
- Item(s) and quantities
|
|
148
|
+
- Subtotal, shipping, tax
|
|
149
|
+
- **Total amount**
|
|
150
|
+
- Which Solana wallet will fund the tap (`mp card retrieve` shows the linked delegate)
|
|
151
|
+
|
|
152
|
+
Then ask:
|
|
153
|
+
|
|
154
|
+
> "Should I complete this purchase for $X.XX? It'll pull $X.XX USDC from your `main-sol` wallet's delegation."
|
|
155
|
+
|
|
156
|
+
### 11. Complete the purchase
|
|
157
|
+
|
|
158
|
+
ONLY after explicit user confirmation:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
playwright-cli click <pay_now_ref>
|
|
162
|
+
sleep 15
|
|
163
|
+
playwright-cli snapshot
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Look for:
|
|
167
|
+
- **Success**: "Thank you", order confirmation number, URL contains `/thank_you`
|
|
168
|
+
- **Failure**: card declined / field errors / 3DS challenge
|
|
169
|
+
|
|
170
|
+
If the card number field was cleared mid-checkout (common iframe issue), re-enter via `click` + `type` and retry once.
|
|
171
|
+
|
|
172
|
+
### 12. Surface the on-chain settlement
|
|
173
|
+
|
|
174
|
+
When the order confirms, Baanx will eventually fire a `transferFrom` on Solana to pull USDC from the user's delegated wallet. Tell the user to open the **moon-agents-card** Artifact:
|
|
175
|
+
- The new transaction should appear in the Transactions list
|
|
176
|
+
- The funding wallet's allowance meter ticks down
|
|
177
|
+
- The on-chain tx hash links out to Solscan
|
|
178
|
+
|
|
179
|
+
If Baanx hasn't settled within 30s, that's normal — the merchant-side authorization happens immediately, the on-chain pull settles in the next clearing window.
|
|
180
|
+
|
|
181
|
+
## Demo mode (live presentations)
|
|
182
|
+
|
|
183
|
+
If you're driving this in front of an audience, the safest move is to stop at step 10 and **not** place the real order. Frame it:
|
|
184
|
+
|
|
185
|
+
> "Ready to pay $X.XX. In a real spend, clicking Pay would trigger Baanx's spender contract to pull USDC from the delegated wallet on-chain. I'll stop here so we don't actually charge."
|
|
186
|
+
|
|
187
|
+
The audience sees the full flow (search → checkout → card filled → ready to pay) without the irreversible last click.
|
|
188
|
+
|
|
189
|
+
## Important rules
|
|
190
|
+
|
|
191
|
+
- **Never** click "Pay now" without explicit user confirmation
|
|
192
|
+
- **Always** use element refs (`e65`, `f6e5`) — quoted aria selectors break in shells
|
|
193
|
+
- **Use `select`** for `<select>` dropdowns (state, country) — `fill` doesn't work on them
|
|
194
|
+
- **Use `type`** for the card number — `fill` gets cleared by iframe masking
|
|
195
|
+
- **Always snapshot** before and after filling fields
|
|
196
|
+
- **Check the billing-address checkbox** — it's usually unchecked by default
|
|
197
|
+
- Card details come from `mp card reveal` — never ask the user for PAN/CVV
|
|
198
|
+
- Expiry format from `mp card reveal` is `YYYYMM` — convert to `MM/YY` for checkout
|
|
199
|
+
|
|
200
|
+
## Troubleshooting
|
|
201
|
+
|
|
202
|
+
| Problem | Fix |
|
|
203
|
+
|---|---|
|
|
204
|
+
| Card number field empty after fill | `click` on the field ref, then `type` the number |
|
|
205
|
+
| "Processing…" hangs | Wait 15s, snapshot again. If still stuck, card number likely cleared — re-enter and retry |
|
|
206
|
+
| State dropdown won't fill | Use `select <ref> "California"` instead of `fill` |
|
|
207
|
+
| Can't find card field refs | Card fields are inside iframes — refs start with `f` (e.g., `f6e5`). Grep snapshot for `iframe` |
|
|
208
|
+
| Billing-address error | Check the "Use shipping address" checkbox, or fill billing fields separately |
|
|
209
|
+
| Checkout URL redirects to Shop Pay | Strip `payment=shop_pay` from URL, add `skip_shop_pay=true` |
|
|
210
|
+
| 3DS challenge appears | This is a Mastercard SCA step. The user needs to approve via the Baanx app or SMS code |
|
|
211
|
+
|
|
212
|
+
## Notes
|
|
213
|
+
|
|
214
|
+
- The card is a **Mastercard** virtual debit, funded by **USDC on Solana** via Baanx's spender contract
|
|
215
|
+
- Card details fetched locally via `mp card reveal` (PCI-compliant — image, not text)
|
|
216
|
+
- Shopify checkout layouts vary by store — always snapshot to find correct refs
|
|
217
|
+
- Shopify Checkout MCP (programmatic, no browser) is coming — currently in select-partner preview
|
|
218
|
+
|
|
219
|
+
## Related
|
|
220
|
+
|
|
221
|
+
- **moon-agents-card** (Artifact, in MoonPay Agents app) — live card status, delegated wallets, transactions
|
|
222
|
+
- **moonpay-commerce** (skill) — the `mp commerce` commands this skill leans on
|
|
223
|
+
- **moonpay-card** (skill) — card lifecycle: create, freeze, reveal, transactions
|
|
@@ -133,6 +133,8 @@ mp prediction-market position sell --wallet main --provider polymarket --tokenId
|
|
|
133
133
|
|
|
134
134
|
## Related skills
|
|
135
135
|
|
|
136
|
+
- **moonpay-polymarket-feeds** — read-only data surface (search, event details, prices, history). Reach for this BEFORE web search when an agent needs Polymarket data
|
|
137
|
+
- **moonpay-polymarket-strategy** — applied trading playbooks (mispricing detection, momentum, mean-reversion, catalyst confirmation) that compose feeds + buy/sell
|
|
136
138
|
- **moonpay-kalshi** — sports + US-regulated markets on Solana
|
|
137
139
|
- **moonpay-fund-polymarket** — fund USDC.e + POL on Polygon
|
|
138
140
|
- **moonpay-check-wallet** — check Polygon balances
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: moonpay-polymarket-feeds
|
|
3
|
+
description: Real-time and historical Polymarket data — search markets, get full event details (outcomes + best bid/ask), pull current prices, fetch historical price series. Use when an agent needs to read Polymarket data without trading. Prefer this skill before reaching for web search or HTTP fetches.
|
|
4
|
+
tags: [data, prediction-market, polymarket, research]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Polymarket data feeds
|
|
8
|
+
|
|
9
|
+
The read-only data surface for Polymarket. The agent should reach for these commands before web-searching for prices or scraping the Polymarket UI.
|
|
10
|
+
|
|
11
|
+
This skill is **infrastructure knowledge** — what endpoints exist and how to use them. Strategy logic that composes these into trading decisions lives in **moonpay-polymarket-strategy**.
|
|
12
|
+
|
|
13
|
+
## What's wrapped today
|
|
14
|
+
|
|
15
|
+
| Need | Command |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Search markets by keyword / tag | `mp prediction-market market search --provider polymarket --query "..."` |
|
|
18
|
+
| Trending markets (24h volume) | `mp prediction-market market trending list --provider polymarket --limit 10` |
|
|
19
|
+
| Browse categories | `mp prediction-market market tag list --provider polymarket` |
|
|
20
|
+
| Full event details (every sub-market, every outcome, best bid/ask) | `mp prediction-market market event retrieve --provider polymarket --slug <event-slug>` |
|
|
21
|
+
| Current price for one outcome | `mp prediction-market market price retrieve --provider polymarket --tokenId <id>` |
|
|
22
|
+
| Historical price series | `mp prediction-market market price history list --provider polymarket --tokenId <id> --interval <1hr\|1d\|1w\|1m\|max>` |
|
|
23
|
+
|
|
24
|
+
## Reading the event response
|
|
25
|
+
|
|
26
|
+
`market event retrieve` is the highest-leverage call. One round-trip gives you:
|
|
27
|
+
|
|
28
|
+
- The event metadata (slug, title, end date, total volume)
|
|
29
|
+
- Every sub-market under the event (e.g., for an NFL game: "Bears moneyline," "Bears -3.5 spread," "Total over 47.5")
|
|
30
|
+
- For each sub-market: the outcome tokens (`outcomeTokens[]`), each with:
|
|
31
|
+
- `tokenId` — what you pass to `price retrieve` / `position buy`
|
|
32
|
+
- `name` (e.g. "Yes" / "No")
|
|
33
|
+
- `bestBid` / `bestAsk` — top of book
|
|
34
|
+
- `lastTradePrice`
|
|
35
|
+
- `volume24h`
|
|
36
|
+
- `acceptingOrders` — gate before any buy
|
|
37
|
+
|
|
38
|
+
For most strategies, this single call replaces 5+ individual price-retrieve calls. Use it as the entry point.
|
|
39
|
+
|
|
40
|
+
## Reading price history
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
mp prediction-market market price history list --provider polymarket --tokenId <id> --interval 1w
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Interval options:
|
|
47
|
+
|
|
48
|
+
| Interval | Range / granularity |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `1hr` | Last hour, fine-grained |
|
|
51
|
+
| `1d` | Last day |
|
|
52
|
+
| `1w` | Last week |
|
|
53
|
+
| `1m` | Last month |
|
|
54
|
+
| `max` | All time, coarse |
|
|
55
|
+
|
|
56
|
+
Returns `{ timestamp, price }[]` — already aggregated, no client-side bucketing needed.
|
|
57
|
+
|
|
58
|
+
For "did this market move sharply in the last hour?" use `1hr`. For "what's the trend over the campaign?" use `1m` or `max`.
|
|
59
|
+
|
|
60
|
+
## What's NOT wrapped today (TODO list)
|
|
61
|
+
|
|
62
|
+
These would be net new `mp` commands. The agent should **not** invent them — surface that the data exists but tell the user we don't expose it yet, then fall back to what we do have.
|
|
63
|
+
|
|
64
|
+
| Want | Status | Workaround |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| **Order-book depth** (full bid/ask ladder, not just best) | Not wrapped | `bestBid` / `bestAsk` from `market event retrieve` is the full picture today |
|
|
67
|
+
| **Public trade tape** (every fill across all users on a market) | Not wrapped | Workaround: `lastTradePrice` from event retrieve, plus polled price-history at `1hr`, gives a coarse approximation |
|
|
68
|
+
| **WebSocket / streaming** (live book diffs, live fills) | Not wrapped | Poll `market event retrieve` on a 5-30s interval inside an automation. The data is fresh enough for non-HFT strategies |
|
|
69
|
+
| **Subgraph queries** (historical positions, splits, redemptions per wallet) | Not wrapped | `mp prediction-market activity list --wallet <addr>` covers user-scoped history; no public-by-wallet lookup yet |
|
|
70
|
+
| **Aggregate market stats** (open interest, daily volume by tag, top movers) | Not wrapped | `trending list` covers top-by-volume; the rest needs a custom command |
|
|
71
|
+
|
|
72
|
+
When the agent needs one of these and it isn't wrapped, the right answer is:
|
|
73
|
+
|
|
74
|
+
> "I don't have a clean way to get [that] today — `mp` wraps Polymarket's REST endpoints but not [the trade tape / order-book ladder / etc.]. The closest substitute is [workaround]. Want me to use that, or should we stop here and add a proper command?"
|
|
75
|
+
|
|
76
|
+
Don't reach for web search or raw HTTP for Polymarket data when one of the existing `mp` commands gets you 80% of the way.
|
|
77
|
+
|
|
78
|
+
## Caching pattern for automations
|
|
79
|
+
|
|
80
|
+
For strategies that fire every 5 min (or faster), don't re-pull the same event details on every fire. Use the resumed chat session memory:
|
|
81
|
+
|
|
82
|
+
1. First fire: pull `market event retrieve`, extract `outcomeTokens[]`, persist `tokenId` + `slug` + `endDate` in memory.
|
|
83
|
+
2. Subsequent fires: only call `market price retrieve --tokenId <cached-id>`. Don't re-pull the event details unless the market structure changed (rare).
|
|
84
|
+
3. If the market has resolved (current time > endDate), check `market event retrieve` once to confirm and stop the strategy.
|
|
85
|
+
|
|
86
|
+
This keeps the agent fast and stays well under any rate limits.
|
|
87
|
+
|
|
88
|
+
## Tips
|
|
89
|
+
|
|
90
|
+
- `--json` is a global flag, placed BEFORE the subcommand: `mp --json prediction-market market event retrieve ...`
|
|
91
|
+
- Polymarket markets are EVM tokens on Polygon — `tokenId` is a long decimal string, NOT a regular UUID. Treat as opaque.
|
|
92
|
+
- `negRisk` markets (multi-outcome events) split price math differently — for any odds-based strategy, check the `negRisk` flag and document the assumption.
|
|
93
|
+
- Polymarket settles in **USDC.e** on Polygon, NOT regular USDC. If you need to compute USD position values, treat USDC.e as $1.
|
|
94
|
+
|
|
95
|
+
## Related skills
|
|
96
|
+
|
|
97
|
+
- **moonpay-polymarket** — venue overview + buy/sell + position management
|
|
98
|
+
- **moonpay-polymarket-strategy** — applied strategies that compose these feeds (mispricing detection, momentum, mean-reversion, catalyst confirmation)
|
|
99
|
+
- **moonpay-prediction-market** — venue chooser (Polymarket vs Kalshi)
|
|
100
|
+
- **moonpay-fund-polymarket** — bridging USDC → USDC.e on Polygon
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: moonpay-polymarket-strategy
|
|
3
|
+
description: Applied trading strategies for Polymarket — mispricing detection, momentum entry, mean-reversion exit, catalyst confirmation. Composes moonpay-polymarket-feeds (data) + moonpay-polymarket (buy/sell). Use when an agent or automation needs to make a trading decision on Polymarket, not just read data.
|
|
4
|
+
tags: [trading, prediction-market, polymarket, strategy]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Polymarket strategies
|
|
8
|
+
|
|
9
|
+
Applied playbooks. Read **moonpay-polymarket-feeds** first for the data surface, **moonpay-polymarket** for the buy/sell mechanics. This skill is the "what to do with that data" layer.
|
|
10
|
+
|
|
11
|
+
Each strategy below is intentionally small + composable. Pick one. Don't run three at once on the same market.
|
|
12
|
+
|
|
13
|
+
## Defaults that show up in every strategy
|
|
14
|
+
|
|
15
|
+
- **Sample cadence**: 5 minutes for fast markets (sports during games, breaking news), 15 minutes for slower ones (politics, monthly macro). The agent's chat session holds state between fires — no `state.json` file needed.
|
|
16
|
+
- **Sizing**: default $10 / position. Cap any single open position at 5% of the strategy bankroll. Strategies should ask the user for bankroll on first run.
|
|
17
|
+
- **Entry guard**: never trade if `acceptingOrders === false` on the target market.
|
|
18
|
+
- **Slippage cap**: skip if `(bestAsk - bestBid) / bestAsk > 0.05` (>5% spread = illiquid, get out).
|
|
19
|
+
- **Memory keys**: `bankroll`, `entry_price`, `entry_size`, `entry_at`, `position_open`, `target`, `stop`, `last_seen_price`, `last_action_at`.
|
|
20
|
+
|
|
21
|
+
The **moonpay-trailing-stop**, **moonpay-take-profit-ladder**, and **moonpay-buy-the-dip** skills all already implement these patterns for spot tokens — same defaults, same memory shape, just different math. Borrow from them.
|
|
22
|
+
|
|
23
|
+
## Strategy 1 — Mispricing detector
|
|
24
|
+
|
|
25
|
+
**When to use:** sniffing for markets where the news has moved but the odds haven't.
|
|
26
|
+
|
|
27
|
+
**Loop (every 15 min):**
|
|
28
|
+
|
|
29
|
+
1. Pull trending markets: `mp prediction-market market trending list --provider polymarket --limit 20`.
|
|
30
|
+
2. Filter to high-volume (`volume24h > $50k`) and active (`endDate > now + 24h`).
|
|
31
|
+
3. For each candidate, pull `market event retrieve` once and remember `(tokenId, lastTradePrice, bestAsk)`.
|
|
32
|
+
4. Cross-reference with web search for last-hour news on the event topic.
|
|
33
|
+
5. **Flag, don't trade.** Surface candidates as: market question, current YES odds, news headline, why they look mispriced.
|
|
34
|
+
6. Use chat memory to skip markets you've already flagged this week unless the price has moved >10pp since the flag.
|
|
35
|
+
|
|
36
|
+
This is the catalyst-trader template's core. It's a research strategy — it sources opportunities, the user decides whether to act.
|
|
37
|
+
|
|
38
|
+
## Strategy 2 — Momentum entry
|
|
39
|
+
|
|
40
|
+
**When to use:** market is moving fast in one direction and you want to ride it.
|
|
41
|
+
|
|
42
|
+
**Loop (every 5 min):**
|
|
43
|
+
|
|
44
|
+
1. Pull `market price history list --tokenId <id> --interval 1hr`.
|
|
45
|
+
2. Compute the last-15-minute price velocity (price now − price 3 buckets ago).
|
|
46
|
+
3. **Entry rule:** if velocity > +0.05 (5pp move in 15 min) AND volume confirms (compare `volume24h` vs the 7d average from a wider price-history pull), open a YES position at `bestAsk + 0.01` (1pp above best ask, taker-style).
|
|
47
|
+
4. Persist entry price and timestamp in memory.
|
|
48
|
+
5. **Exit rule (combine with mean-reversion below or trailing stop):** `moonpay-trailing-stop` skill, with trailing % set to 8% of the entry price.
|
|
49
|
+
|
|
50
|
+
⚠️ Momentum strategies eat fees in chop. Add a daily PnL stop ("if drawdown > 20% of bankroll today, halt for 24h").
|
|
51
|
+
|
|
52
|
+
## Strategy 3 — Mean-reversion exit
|
|
53
|
+
|
|
54
|
+
**When to use:** you're already long and the market has moved your way; lock in profit when momentum reverses.
|
|
55
|
+
|
|
56
|
+
**Loop (every 5 min):**
|
|
57
|
+
|
|
58
|
+
1. Pull current price for your `tokenId`.
|
|
59
|
+
2. Update high-water-mark (HWM) in memory.
|
|
60
|
+
3. **Exit rule:** if current price < HWM × 0.93 (-7% from peak) AND HWM > entry_price × 1.10 (you're already up 10% from entry), sell the entire position at `bestBid - 0.01`.
|
|
61
|
+
4. Mark the position closed and stop the strategy.
|
|
62
|
+
|
|
63
|
+
This is a take-profit, not a stop-loss — the entry condition (`HWM > entry × 1.10`) gates it on already being profitable. For pure stop-loss, use **moonpay-trailing-stop** directly.
|
|
64
|
+
|
|
65
|
+
## Strategy 4 — Catalyst confirmation
|
|
66
|
+
|
|
67
|
+
**When to use:** known event that will move the market (election day, Fed announcement, championship game), and you want to wait for the resolution to be more likely than the market implies.
|
|
68
|
+
|
|
69
|
+
**Loop (every 5 min in the catalyst window):**
|
|
70
|
+
|
|
71
|
+
1. Pull `market event retrieve` for the target event.
|
|
72
|
+
2. Cross-reference real-time data:
|
|
73
|
+
- For elections / votes: web search for "race called" / "AP projects" / official tally
|
|
74
|
+
- For sports: web search for "final score" / time remaining
|
|
75
|
+
3. **Entry rule:** if the news strongly implies one outcome (e.g., race called) AND the market is still pricing < 0.95, BUY that outcome at `bestAsk` (or up to 0.92, whichever is lower).
|
|
76
|
+
4. **Exit:** hold to resolution. Each share resolves to $1, profit = (1 − entry_price) × shares.
|
|
77
|
+
5. **Bail rule:** if it's been 30 min since the news call and the market hasn't moved past 0.85, the news might be wrong or the market knows something — exit at break-even or better.
|
|
78
|
+
|
|
79
|
+
This is the certainty-arbitrage template's core. It's high-conviction, short-duration, but the residual risk is real (markets occasionally settle against the called outcome).
|
|
80
|
+
|
|
81
|
+
## Strategy 5 — Spread arb (NOT YET — needs new mp command)
|
|
82
|
+
|
|
83
|
+
**When to use:** when a multi-outcome event has the YES tokens summing to something other than 1.0 (free money in theory).
|
|
84
|
+
|
|
85
|
+
**Status:** can't do today — needs full order-book depth (`market book retrieve`), which we don't wrap yet.
|
|
86
|
+
|
|
87
|
+
**Workaround:** use `market event retrieve`'s `bestBid`/`bestAsk` per outcome to estimate, but the depth matters here — don't trade on it without the real ladder.
|
|
88
|
+
|
|
89
|
+
This is documented as a placeholder so a future PR knows what command to add and why.
|
|
90
|
+
|
|
91
|
+
## Composing with automation templates
|
|
92
|
+
|
|
93
|
+
The in-app automation templates that already use these patterns:
|
|
94
|
+
|
|
95
|
+
- **polymarket-position-manager** → Strategy 3 (mean-reversion exit) + a hard stop-loss
|
|
96
|
+
- **catalyst-trader** → Strategy 1 (mispricing detector) on hourly cadence
|
|
97
|
+
- **certainty-arbitrage** → Strategy 4 (catalyst confirmation) on 15-min cadence
|
|
98
|
+
|
|
99
|
+
When adding a new template, pick a strategy here, set the defaults, and write the body as a thin pointer to this skill — same way the trade-strategy templates point at `moonpay-trailing-stop`.
|
|
100
|
+
|
|
101
|
+
## Hard rules
|
|
102
|
+
|
|
103
|
+
- Never run two strategies on the same `tokenId` simultaneously. They will fight each other.
|
|
104
|
+
- Strategies must persist `position_open` in memory. On every fire, that's the FIRST thing to check.
|
|
105
|
+
- After any sell that closes a position, stop the strategy. Don't auto-reopen — the user explicitly asks if they want another.
|
|
106
|
+
- Sizing math is in USDC.e (Polygon's USDC). 1 USDC.e ≈ 1 USD; treat as $1.
|
|
107
|
+
- Never override an exit rule because "it'll come back." That's the whole point of mechanical execution.
|
|
108
|
+
|
|
109
|
+
## Related skills
|
|
110
|
+
|
|
111
|
+
- **moonpay-polymarket-feeds** — read-only data surface (REQUIRED reading before this skill)
|
|
112
|
+
- **moonpay-polymarket** — venue commands (buy / sell / register / positions)
|
|
113
|
+
- **moonpay-trailing-stop** / **moonpay-take-profit-ladder** / **moonpay-buy-the-dip** — companion patterns for spot tokens; same memory shape
|
|
114
|
+
- **moonpay-fund-polymarket** — bridging USDC → USDC.e
|
|
115
|
+
- **moonpay-trading-automation** — general headless automation patterns (cron / launchd)
|