@oracle-agent/oracle 0.28.27 → 0.28.28
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/assets/skills/oracle-chain-graphs-telegram-cards/SKILL.md +2 -0
- package/dist/assets/skills/oracle-desk-product/SKILL.md +1 -1
- package/dist/bin/oracle-gateway.mjs +16 -15
- package/dist/cli/commands/signer.mjs +8 -8
- package/dist/local-signer/index.mjs +3 -3
- package/dist/local-signer/runtime.mjs +8 -8
- package/dist/nft/index.mjs +1 -1
- package/package.json +1 -1
package/dist/nft/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function re(t,e="value"){if(typeof t=="number"){if(!Number.isFinite(t))throw new Error(`${e} must be a finite number`);if(Math.abs(t)>Number.MAX_SAFE_INTEGER)throw new Error(`${e} exceeds safe integer precision \u2014 pass it as a decimal string or BigInt so the exact value is preserved`);if(Number.isInteger(t)&&!Number.isSafeInteger(t))throw new Error(`${e} exceeds safe integer precision \u2014 pass it as a decimal string or BigInt so the exact value is preserved`);return String(t)}if(typeof t=="string"){if(t!==t.trim())throw new Error(`${e} must be a number or decimal string`);return t}throw new Error(`${e} must be a number or decimal string`)}function M(t,e="amount"){if(typeof t=="bigint")return t;if(typeof t=="number"){if(!Number.isInteger(t))throw new Error(`${e} must be an integer`);if(!Number.isSafeInteger(t))throw new Error(`${e} exceeds safe integer precision \u2014 pass it as a decimal string or BigInt so the exact value is preserved`);return BigInt(t)}let r=re(t,e);if(r==="")throw new Error(`${e} is required`);if(!/^-?\d+$/.test(r)&&!/^0x[0-9a-fA-F]+$/.test(r))throw new Error(`${e} must be an integer in decimal or 0x-hex form, got ${JSON.stringify(r)}`);try{return BigInt(r)}catch{throw new Error(`${e} must be an integer, got ${JSON.stringify(r)}`)}}var _=1,o=Object.freeze({BUY:"BUY",PASS:"PASS",NO_TRADE:"NO_TRADE"}),j=Object.freeze(["reveal","repriced","reprice","price_drop","relisted"]),F=10000n,G=Object.freeze({minTraitSales:3,minTraitVolumeWei:"1000000000000000000",minVolumeCoverage:3,minEdgeBps:1200,minEdgeBpsWithoutEvent:2e3,maxPremiumOverCollectionFloorBps:5e3,maxListingAgeMs:900*1e3,maxEventAgeMs:1800*1e3,maxMarketDataAgeMs:3600*1e3,maxTraitWindowHours:72,marketplaceFeeBps:250,royaltyBps:500,maxSpendWei:null});function P(t,e,r,{positive:i=!0,allowZero:p=!1}={}){if(t==null||t==="")return r.push(`${e}: missing`),null;let n;try{n=M(t,e)}catch(a){return r.push(`${e}: ${String(a?.message||a)}`),null}return n<0n?(r.push(`${e}: must not be negative`),null):i&&!p&&n===0n?(r.push(`${e}: must be positive`),null):n}function ie(t,e,r){if(t==null||t==="")return r.push(`${e}: missing`),null;let i=Number(t);return!Number.isInteger(i)||i<0?(r.push(`${e}: must be a non-negative integer`),null):i}function x(t){let e=Number(t);return Number.isFinite(e)?e:null}function u(...t){for(let e of t)if(e!=null&&e!=="")return e;return null}function C(t,e){return e==null||e===0n?null:Number(t*F/e)}function ne(t){let e=x(u(t?.rarityPct,t?.rarity_pct));if(e!=null&&e>0)return Math.round(e*100);let r=x(u(t?.traitSupply,t?.supply,t?.count)),i=x(u(t?.collectionSupply,t?.totalSupply));return r!=null&&i!=null&&r>0&&i>0?Math.round(r/i*1e4):null}function oe(t={}){let e={...G,...t||{}},r=Number(e.marketplaceFeeBps)||0,i=Number(e.royaltyBps)||0;if(r<0||i<0||r+i>=1e4)throw new Error("trait sniper: marketplaceFeeBps + royaltyBps must be >= 0 and < 10000");return e}function d(t,e,r,i={}){return{ok:t===o.BUY,decision:t,failureKind:e,reasons:r,version:_,executed:!1,signed:!1,broadcast:!1,requiresUserSignature:!0,...i}}function U(t={}){let e;try{e=oe(t.policy)}catch(w){return d(o.NO_TRADE,"bad-policy",[String(w?.message||w)])}let r=Number.isFinite(Number(t.nowMs))?Number(t.nowMs):Date.now(),i=t.listing||{},p=t.trait||{},n=t.traitMarket||t.traitStats||{},a=t.collectionMarket||t.collection||{},c={chainId:x(u(t.chainId,i.chainId))??null,contract:String(u(t.contract,t.collectionAddress,i.contract,"")||"")||null,tokenId:String(u(i.tokenId,t.tokenId,"")||"")||null,marketplace:String(u(i.marketplace,i.source,"")||"")||null,trait:{type:String(u(p.type,p.traitType,"")||"")||null,value:String(u(p.value,p.traitValue,"")||"")||null,rarityBps:ne(p)}},l=[],s=P(u(i.priceWei,i.price),"listing.priceWei",l),h=P(u(n.floorPriceWei,n.floorWei),"traitMarket.floorPriceWei",l),y=P(u(n.volumeWei,n.volume),"traitMarket.volumeWei",l,{allowZero:!0}),A=P(u(a.floorPriceWei,a.floorWei),"collectionMarket.floorPriceWei",l),b=ie(u(n.sales,n.salesCount),"traitMarket.sales",l),g=x(u(n.windowHours,n.window_hours));if((g==null||g<=0)&&l.push("traitMarket.windowHours: missing"),l.length)return d(o.NO_TRADE,"insufficient-data",[`missing or unusable market data \u2014 ${l.join("; ")}`],{identity:c,dataGaps:l,liquidity:null,edge:null});if(g>e.maxTraitWindowHours)return d(o.NO_TRADE,"window-too-wide",[`trait stats aggregate ${g}h > maxTraitWindowHours ${e.maxTraitWindowHours}h \u2014 not recent liquidity`],{identity:c,dataGaps:[],liquidity:null,edge:null});let T=x(u(n.observedAtMs,n.asOfMs,a.observedAtMs));if(T==null)return d(o.NO_TRADE,"insufficient-data",["missing or unusable market data \u2014 traitMarket.observedAtMs: missing"],{identity:c,dataGaps:["traitMarket.observedAtMs: missing"],liquidity:null,edge:null});let E=r-T;if(E>e.maxMarketDataAgeMs)return d(o.NO_TRADE,"stale-market-data",[`market snapshot is ${E}ms old > maxMarketDataAgeMs ${e.maxMarketDataAgeMs}ms`],{identity:c,marketAgeMs:E,liquidity:null,edge:null});if(i.flagged===!0||i.reported===!0)return d(o.NO_TRADE,"listing-flagged",["listing is flagged/reported by the marketplace \u2014 a cheap flagged token is cheap for a reason"],{identity:c,liquidity:null,edge:null});let z=y>=s*BigInt(Math.max(0,Math.trunc(e.minVolumeCoverage))),R=M(e.minTraitVolumeWei??0,"policy.minTraitVolumeWei"),W={sales:b,minSales:e.minTraitSales,volumeWei:y.toString(),minVolumeWei:R.toString(),windowHours:g,coverageX:s===0n?null:Number(y/s),minCoverageX:e.minVolumeCoverage,illiquid:!1},$=[];if(b<e.minTraitSales&&$.push(`only ${b} trait sale(s) in ${g}h, need ${e.minTraitSales}`),y<R&&$.push(`trait volume ${y} wei below minTraitVolumeWei ${R} wei`),z||$.push(`trait volume ${y} wei does not cover ${e.minVolumeCoverage}x the ${s} wei listing \u2014 no exit depth`),$.length){W.illiquid=!0;let w=c.trait.rarityBps;return w!=null&&w<=100&&$.push(`trait is rare (${w} bps of supply) but rarity is ranking-only \u2014 an unsold rare trait is an illiquid bag, not an edge`),d(o.NO_TRADE,"illiquid-trait",$,{identity:c,liquidity:W,edge:null,marketAgeMs:E})}let O=String(u(t.event?.kind,t.event?.type,i.eventKind,"")||"").toLowerCase(),V=x(u(t.event?.atMs,i.eventAtMs)),v=V==null?null:r-V,N=j.includes(O)&&v!=null&&v>=0&&v<=e.maxEventAgeMs,B=N?e.minEdgeBps:e.minEdgeBpsWithoutEvent,Z=BigInt(Math.trunc(e.marketplaceFeeBps)),Q=BigInt(Math.trunc(e.royaltyBps)),H=h*(F-Z-Q)/F,q=H-s,ee=C(h-s,h),I=C(s-A,A),k=C(q,s),te={listingPriceWei:s.toString(),traitFloorWei:h.toString(),collectionFloorWei:A.toString(),netExitWei:H.toString(),edgeWei:q.toString(),edgeBps:k,requiredEdgeBps:B,discountToTraitFloorBps:ee,premiumToCollectionFloorBps:I,feeBps:e.marketplaceFeeBps,royaltyBps:e.royaltyBps,triggered:N,eventKind:O||null,eventAgeMs:v},S={identity:c,liquidity:W,edge:te,marketAgeMs:E},L=x(u(i.listedAtMs,i.listedAt)),m=L==null?null:r-L;if(i.stale===!0)return d(o.PASS,"listing-stale",["listing is marked stale by the caller"],S);if(m==null)return d(o.NO_TRADE,"insufficient-data",["missing or unusable market data \u2014 listing.listedAtMs: missing"],{...S,dataGaps:["listing.listedAtMs: missing"]});if(m<0)return d(o.NO_TRADE,"listing-in-future",[`listing.listedAtMs is ${-m}ms in the future`],S);if(m>e.maxListingAgeMs)return d(o.PASS,"listing-stale",[`listing is ${m}ms old > maxListingAgeMs ${e.maxListingAgeMs}ms \u2014 the mispricing is not live`],{...S,listingAgeMs:m});if(e.maxSpendWei!=null&&e.maxSpendWei!==""){let w=M(e.maxSpendWei,"policy.maxSpendWei");if(s>w)return d(o.PASS,"spend-cap",[`listing ${s} wei exceeds maxSpendWei ${w} wei`],{...S,listingAgeMs:m})}return s>=h?d(o.PASS,"no-discount",[`listing ${s} wei is at or above the trait floor ${h} wei`],{...S,listingAgeMs:m}):I!=null&&I>e.maxPremiumOverCollectionFloorBps?d(o.PASS,"collection-floor-premium",[`listing sits ${I} bps over the collection floor > maxPremiumOverCollectionFloorBps ${e.maxPremiumOverCollectionFloorBps} bps`],{...S,listingAgeMs:m}):q<=0n||k==null||k<B?d(o.PASS,"edge-below-threshold",[`net edge ${k??0} bps after ${e.marketplaceFeeBps}+${e.royaltyBps} bps costs is below the ${B} bps required${N?"":" without a reveal/reprice trigger"}`],{...S,listingAgeMs:m}):d(o.BUY,null,[`${b} trait sale(s) and ${y} wei of volume in ${g}h clear the liquidity gate`,`net edge ${k} bps vs trait floor after ${e.marketplaceFeeBps}+${e.royaltyBps} bps costs, required ${B} bps`,N?`${O} event ${v}ms ago explains the mispricing`:"no trigger event \u2014 cleared the higher untriggered bar"],{...S,listingAgeMs:m})}function se(t=[],e={}){if(!Array.isArray(t))throw new TypeError("scanTraitListings: listings array required");let r=t.map((n,a)=>{let c={...e,...n,policy:{...e.policy||{},...n?.policy||{}}},l=U(c);return{index:a,...l}}),i=r.filter(n=>n.decision===o.BUY),p=[...i].sort((n,a)=>{let c=(a.edge?.edgeBps??0)-(n.edge?.edgeBps??0);if(c!==0)return c;let l=n.identity?.trait?.rarityBps??Number.MAX_SAFE_INTEGER,s=a.identity?.trait?.rarityBps??Number.MAX_SAFE_INTEGER;return l!==s?l-s:n.index-a.index});return{evaluated:r,ranked:p,counts:{total:r.length,buy:i.length,pass:r.filter(n=>n.decision===o.PASS).length,noTrade:r.filter(n=>n.decision===o.NO_TRADE).length},executed:!1,signed:!1,broadcast:!1}}import{createHash as ae,createHmac as ce,timingSafeEqual as be}from"node:crypto";var le=2,ue=300*1e3,pe=(()=>{let t=Number(process.env.ORACLE_PREPARE_HARD_MAX_AGE_MS);return Number.isFinite(t)&&t>0?t:1800*1e3})();function D(t){return t===null||typeof t!="object"?JSON.stringify(t):Array.isArray(t)?`[${t.map(D).join(",")}]`:`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${D(t[r])}`).join(",")}}`}function Y(t){let e=String(t?.ORACLE_STAMP_HMAC_SECRET||"").trim();return e.length>=16?e:""}function X(t=process.env){return Y(process.env)||Y(t)}function de(t){let{prepareHash:e,prepareMac:r,note:i,...p}=t||{};return p}function me(t){let e=de(t);return ae("sha256").update(D(e)).digest("hex")}function ge(t,e=X()){return e?ce("sha256",e).update(String(t)).digest("hex"):""}function J(t,{provider:e,kind:r,env:i=process.env,nowMs:p=Date.now(),expiresAtMs:n}={}){if(!t||typeof t!="object")throw new Error("stampPrepared: payload object required");let a=Object.prototype.hasOwnProperty.call(t,"oraclePrepared");if(a&&typeof t.oraclePrepared!="boolean")throw new Error("stampPrepared: oraclePrepared must be boolean");let c={...t,provider:t.provider||e,kind:t.kind||r,requiresUserSignature:t.requiresUserSignature!==!1,signingReady:!1,broadcastReady:!1,executionReady:!1,oraclePrepared:a?t.oraclePrepared:!0};if(!c.provider)throw new Error("stampPrepared: provider required");if(!c.kind)throw new Error("stampPrepared: kind required");let l=Number(p),s=n==null?l+ue:Number(n),h=s-l;if(!Number.isSafeInteger(l)||!Number.isSafeInteger(s)||h<=0||h>pe)throw new Error("stampPrepared: bounded preparedAt/expiresAt required");let A={...c,preparedAt:l,expiresAt:s,prepareVersion:le},b=me(A),g=X(i),T=g?ge(b,g):void 0,E={...A,prepareHash:b};return T&&(E.prepareMac=T),E}var fe=/^0x[0-9a-fA-F]{40}$/,he=10000n,f=class extends Error{constructor(e,r){super(e),this.name="TraitSnipePrepareError",this.failureKind=r}};function K(t={}){let e=t.verdict;if(!e||typeof e!="object")throw new f("prepareTraitSnipeCandidate: verdict object required","verdict-required");if(e.decision!==o.BUY||e.ok!==!0)throw new f(`prepareTraitSnipeCandidate: refusing to prepare a ${e.decision||"unknown"} verdict \u2014 only BUY is preparable`,"not-a-buy");if(!e.edge||!e.liquidity||e.liquidity.illiquid===!0)throw new f("prepareTraitSnipeCandidate: verdict is missing its edge/liquidity evidence","evidence-required");let r=String(t.buyer||"").trim();if(!fe.test(r))throw new f("prepareTraitSnipeCandidate: buyer must be a 0x EVM address \u2014 Oracle never derives it from a key","buyer-required");let i=e.identity||{};if(!i.contract||!i.tokenId)throw new f("prepareTraitSnipeCandidate: verdict identity needs contract and tokenId","identity-required");let p=Number(i.chainId);if(!Number.isInteger(p)||p<=0)throw new f("prepareTraitSnipeCandidate: chainId required","chain-required");let n=M(e.edge.listingPriceWei,"listingPriceWei"),a=BigInt(Math.max(0,Math.trunc(Number(t.maxSlippageBps??0))));if(a>1000n)throw new f("prepareTraitSnipeCandidate: maxSlippageBps is capped at 1000","slippage-cap");let c=n+n*a/he;return J({provider:"nft-trait-sniper",kind:"nft-trait-snipe-intent",traitSniperVersion:_,chainId:p,contract:i.contract,tokenId:i.tokenId,marketplace:i.marketplace,trait:i.trait,buyer:r,intent:{action:"nft-buy-listing",chainId:p,contract:i.contract,tokenId:i.tokenId,marketplace:i.marketplace,listingPriceWei:n.toString(),maxPriceWei:c.toString(),maxSlippageBps:Number(a),buyer:r},transaction:null,calldata:null,decision:e.decision,reasons:e.reasons,liquidity:e.liquidity,edge:e.edge,requiresUserSignature:!0,requiresExternalFillRoute:!0,marketplaceContacted:!1,orderSubmitted:!1,note:"Unsigned intent metadata only. Oracle did not contact a marketplace, build fill calldata, sign, or broadcast. A user-controlled client must source the order and the user must sign it."},{provider:"nft-trait-sniper",kind:"nft-trait-snipe-intent"})}function Se(t={},e={}){let r=Array.isArray(t.ranked)?t.ranked[0]:null;return r?{candidate:K({...e,verdict:r}),verdict:r}:{candidate:null,verdict:null}}export{G as DEFAULT_TRAIT_SNIPE_POLICY,o as TRAIT_DECISIONS,_ as TRAIT_SNIPER_VERSION,j as TRIGGER_EVENTS,f as TraitSnipePrepareError,U as evaluateTraitListing,Se as prepareTopTraitSnipe,K as prepareTraitSnipeCandidate,se as scanTraitListings};
|
|
1
|
+
function se(e,t="value"){if(typeof e=="number"){if(!Number.isFinite(e))throw new Error(`${t} must be a finite number`);if(Math.abs(e)>Number.MAX_SAFE_INTEGER)throw new Error(`${t} exceeds safe integer precision \u2014 pass it as a decimal string or BigInt so the exact value is preserved`);if(Number.isInteger(e)&&!Number.isSafeInteger(e))throw new Error(`${t} exceeds safe integer precision \u2014 pass it as a decimal string or BigInt so the exact value is preserved`);return String(e)}if(typeof e=="string"){if(e!==e.trim())throw new Error(`${t} must be a number or decimal string`);return e}throw new Error(`${t} must be a number or decimal string`)}function q(e,t="amount"){if(typeof e=="bigint")return e;if(typeof e=="number"){if(!Number.isInteger(e))throw new Error(`${t} must be an integer`);if(!Number.isSafeInteger(e))throw new Error(`${t} exceeds safe integer precision \u2014 pass it as a decimal string or BigInt so the exact value is preserved`);return BigInt(e)}let r=se(e,t);if(r==="")throw new Error(`${t} is required`);if(!/^-?\d+$/.test(r)&&!/^0x[0-9a-fA-F]+$/.test(r))throw new Error(`${t} must be an integer in decimal or 0x-hex form, got ${JSON.stringify(r)}`);try{return BigInt(r)}catch{throw new Error(`${t} must be an integer, got ${JSON.stringify(r)}`)}}var j=1,c=Object.freeze({BUY:"BUY",PASS:"PASS",NO_TRADE:"NO_TRADE"}),K=Object.freeze(["reveal","repriced","reprice","price_drop","relisted"]),J=10000n,Z=Object.freeze({minTraitSales:3,minTraitVolumeWei:"1000000000000000000",minVolumeCoverage:3,minEdgeBps:1200,minEdgeBpsWithoutEvent:2e3,maxPremiumOverCollectionFloorBps:5e3,maxListingAgeMs:900*1e3,maxEventAgeMs:1800*1e3,maxMarketDataAgeMs:3600*1e3,maxTraitWindowHours:72,marketplaceFeeBps:250,royaltyBps:500,maxSpendWei:null});function U(e,t,r,{positive:n=!0,allowZero:o=!1}={}){if(e==null||e==="")return r.push(`${t}: missing`),null;let i;try{i=q(e,t)}catch(s){return r.push(`${t}: ${String(s?.message||s)}`),null}return i<0n?(r.push(`${t}: must not be negative`),null):n&&!o&&i===0n?(r.push(`${t}: must be positive`),null):i}function ce(e,t,r){if(e==null||e==="")return r.push(`${t}: missing`),null;let n=Number(e);return!Number.isInteger(n)||n<0?(r.push(`${t}: must be a non-negative integer`),null):n}function _(e){let t=Number(e);return Number.isFinite(t)?t:null}function p(...e){for(let t of e)if(t!=null&&t!=="")return t;return null}function Y(e,t){return t==null||t===0n?null:Number(e*J/t)}function le(e){let t=_(p(e?.rarityPct,e?.rarity_pct));if(t!=null&&t>0)return Math.round(t*100);let r=_(p(e?.traitSupply,e?.supply,e?.count)),n=_(p(e?.collectionSupply,e?.totalSupply));return r!=null&&n!=null&&r>0&&n>0?Math.round(r/n*1e4):null}function ue(e={}){let t={...Z,...e||{}},r=Number(t.marketplaceFeeBps)||0,n=Number(t.royaltyBps)||0;if(r<0||n<0||r+n>=1e4)throw new Error("trait sniper: marketplaceFeeBps + royaltyBps must be >= 0 and < 10000");return t}function g(e,t,r,n={}){return{ok:e===c.BUY,decision:e,failureKind:t,reasons:r,version:j,executed:!1,signed:!1,broadcast:!1,requiresUserSignature:!0,...n}}function Q(e={}){let t;try{t=ue(e.policy)}catch(N){return g(c.NO_TRADE,"bad-policy",[String(N?.message||N)])}let r=Number.isFinite(Number(e.nowMs))?Number(e.nowMs):Date.now(),n=e.listing||{},o=e.trait||{},i=e.traitMarket||e.traitStats||{},s=e.collectionMarket||e.collection||{},l={chainId:_(p(e.chainId,n.chainId))??null,contract:String(p(e.contract,e.collectionAddress,n.contract,"")||"")||null,tokenId:String(p(n.tokenId,e.tokenId,"")||"")||null,marketplace:String(p(n.marketplace,n.source,"")||"")||null,trait:{type:String(p(o.type,o.traitType,"")||"")||null,value:String(p(o.value,o.traitValue,"")||"")||null,rarityBps:le(o)}},u=[],a=U(p(n.priceWei,n.price),"listing.priceWei",u),m=U(p(i.floorPriceWei,i.floorWei),"traitMarket.floorPriceWei",u),x=U(p(i.volumeWei,i.volume),"traitMarket.volumeWei",u,{allowZero:!0}),P=U(p(s.floorPriceWei,s.floorWei),"collectionMarket.floorPriceWei",u),M=ce(p(i.sales,i.salesCount),"traitMarket.sales",u),w=_(p(i.windowHours,i.window_hours));if((w==null||w<=0)&&u.push("traitMarket.windowHours: missing"),u.length)return g(c.NO_TRADE,"insufficient-data",[`missing or unusable market data \u2014 ${u.join("; ")}`],{identity:l,dataGaps:u,liquidity:null,edge:null});if(w>t.maxTraitWindowHours)return g(c.NO_TRADE,"window-too-wide",[`trait stats aggregate ${w}h > maxTraitWindowHours ${t.maxTraitWindowHours}h \u2014 not recent liquidity`],{identity:l,dataGaps:[],liquidity:null,edge:null});let $=_(p(i.observedAtMs,i.asOfMs,s.observedAtMs));if($==null)return g(c.NO_TRADE,"insufficient-data",["missing or unusable market data \u2014 traitMarket.observedAtMs: missing"],{identity:l,dataGaps:["traitMarket.observedAtMs: missing"],liquidity:null,edge:null});let y=r-$;if(y>t.maxMarketDataAgeMs)return g(c.NO_TRADE,"stale-market-data",[`market snapshot is ${y}ms old > maxMarketDataAgeMs ${t.maxMarketDataAgeMs}ms`],{identity:l,marketAgeMs:y,liquidity:null,edge:null});if(n.flagged===!0||n.reported===!0)return g(c.NO_TRADE,"listing-flagged",["listing is flagged/reported by the marketplace \u2014 a cheap flagged token is cheap for a reason"],{identity:l,liquidity:null,edge:null});let G=x>=a*BigInt(Math.max(0,Math.trunc(t.minVolumeCoverage))),R=q(t.minTraitVolumeWei??0,"policy.minTraitVolumeWei"),B={sales:M,minSales:t.minTraitSales,volumeWei:x.toString(),minVolumeWei:R.toString(),windowHours:w,coverageX:a===0n?null:Number(x/a),minCoverageX:t.minVolumeCoverage,illiquid:!1},v=[];if(M<t.minTraitSales&&v.push(`only ${M} trait sale(s) in ${w}h, need ${t.minTraitSales}`),x<R&&v.push(`trait volume ${x} wei below minTraitVolumeWei ${R} wei`),G||v.push(`trait volume ${x} wei does not cover ${t.minVolumeCoverage}x the ${a} wei listing \u2014 no exit depth`),v.length){B.illiquid=!0;let N=l.trait.rarityBps;return N!=null&&N<=100&&v.push(`trait is rare (${N} bps of supply) but rarity is ranking-only \u2014 an unsold rare trait is an illiquid bag, not an edge`),g(c.NO_TRADE,"illiquid-trait",v,{identity:l,liquidity:B,edge:null,marketAgeMs:y})}let S=String(p(e.event?.kind,e.event?.type,n.eventKind,"")||"").toLowerCase(),D=_(p(e.event?.atMs,n.eventAtMs)),F=D==null?null:r-D,k=K.includes(S)&&F!=null&&F>=0&&F<=t.maxEventAgeMs,O=k?t.minEdgeBps:t.minEdgeBpsWithoutEvent,L=BigInt(Math.trunc(t.marketplaceFeeBps)),V=BigInt(Math.trunc(t.royaltyBps)),H=m*(J-L-V)/J,C=H-a,X=Y(m-a,m),I=Y(a-P,P),A=Y(C,a),f={listingPriceWei:a.toString(),traitFloorWei:m.toString(),collectionFloorWei:P.toString(),netExitWei:H.toString(),edgeWei:C.toString(),edgeBps:A,requiredEdgeBps:O,discountToTraitFloorBps:X,premiumToCollectionFloorBps:I,feeBps:t.marketplaceFeeBps,royaltyBps:t.royaltyBps,triggered:k,eventKind:S||null,eventAgeMs:F},h={identity:l,liquidity:B,edge:f,marketAgeMs:y},b=_(p(n.listedAtMs,n.listedAt)),d=b==null?null:r-b;if(n.stale===!0)return g(c.PASS,"listing-stale",["listing is marked stale by the caller"],h);if(d==null)return g(c.NO_TRADE,"insufficient-data",["missing or unusable market data \u2014 listing.listedAtMs: missing"],{...h,dataGaps:["listing.listedAtMs: missing"]});if(d<0)return g(c.NO_TRADE,"listing-in-future",[`listing.listedAtMs is ${-d}ms in the future`],h);if(d>t.maxListingAgeMs)return g(c.PASS,"listing-stale",[`listing is ${d}ms old > maxListingAgeMs ${t.maxListingAgeMs}ms \u2014 the mispricing is not live`],{...h,listingAgeMs:d});if(t.maxSpendWei!=null&&t.maxSpendWei!==""){let N=q(t.maxSpendWei,"policy.maxSpendWei");if(a>N)return g(c.PASS,"spend-cap",[`listing ${a} wei exceeds maxSpendWei ${N} wei`],{...h,listingAgeMs:d})}return a>=m?g(c.PASS,"no-discount",[`listing ${a} wei is at or above the trait floor ${m} wei`],{...h,listingAgeMs:d}):I!=null&&I>t.maxPremiumOverCollectionFloorBps?g(c.PASS,"collection-floor-premium",[`listing sits ${I} bps over the collection floor > maxPremiumOverCollectionFloorBps ${t.maxPremiumOverCollectionFloorBps} bps`],{...h,listingAgeMs:d}):C<=0n||A==null||A<O?g(c.PASS,"edge-below-threshold",[`net edge ${A??0} bps after ${t.marketplaceFeeBps}+${t.royaltyBps} bps costs is below the ${O} bps required${k?"":" without a reveal/reprice trigger"}`],{...h,listingAgeMs:d}):g(c.BUY,null,[`${M} trait sale(s) and ${x} wei of volume in ${w}h clear the liquidity gate`,`net edge ${A} bps vs trait floor after ${t.marketplaceFeeBps}+${t.royaltyBps} bps costs, required ${O} bps`,k?`${S} event ${F}ms ago explains the mispricing`:"no trigger event \u2014 cleared the higher untriggered bar"],{...h,listingAgeMs:d})}function de(e=[],t={}){if(!Array.isArray(e))throw new TypeError("scanTraitListings: listings array required");let r=e.map((i,s)=>{let l={...t,...i,policy:{...t.policy||{},...i?.policy||{}}},u=Q(l);return{index:s,...u}}),n=r.filter(i=>i.decision===c.BUY),o=[...n].sort((i,s)=>{let l=(s.edge?.edgeBps??0)-(i.edge?.edgeBps??0);if(l!==0)return l;let u=i.identity?.trait?.rarityBps??Number.MAX_SAFE_INTEGER,a=s.identity?.trait?.rarityBps??Number.MAX_SAFE_INTEGER;return u!==a?u-a:i.index-s.index});return{evaluated:r,ranked:o,counts:{total:r.length,buy:n.length,pass:r.filter(i=>i.decision===c.PASS).length,noTrade:r.filter(i=>i.decision===c.NO_TRADE).length},executed:!1,signed:!1,broadcast:!1}}import{createHash as me,createHmac as pe,timingSafeEqual as Fe}from"node:crypto";var fe=2,ge=300*1e3,he=(()=>{let e=Number(process.env.ORACLE_PREPARE_HARD_MAX_AGE_MS);return Number.isFinite(e)&&e>0?e:1800*1e3})();function z(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?`[${e.map(z).join(",")}]`:`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${z(e[r])}`).join(",")}}`}function ee(e){let t=String(e?.ORACLE_STAMP_HMAC_SECRET||"").trim();return t.length>=16?t:""}function te(e=process.env){return ee(process.env)||ee(e)}function we(e){let{prepareHash:t,prepareMac:r,note:n,...o}=e||{};return o}function xe(e){let t=we(e);return me("sha256").update(z(t)).digest("hex")}function Se(e,t=te()){return t?pe("sha256",t).update(String(e)).digest("hex"):""}function re(e,{provider:t,kind:r,env:n=process.env,nowMs:o=Date.now(),expiresAtMs:i}={}){if(!e||typeof e!="object")throw new Error("stampPrepared: payload object required");let s=Object.prototype.hasOwnProperty.call(e,"oraclePrepared");if(s&&typeof e.oraclePrepared!="boolean")throw new Error("stampPrepared: oraclePrepared must be boolean");let l={...e,provider:e.provider||t,kind:e.kind||r,requiresUserSignature:e.requiresUserSignature!==!1,signingReady:!1,broadcastReady:!1,executionReady:!1,oraclePrepared:s?e.oraclePrepared:!0};if(!l.provider)throw new Error("stampPrepared: provider required");if(!l.kind)throw new Error("stampPrepared: kind required");let u=Number(o),a=i==null?u+ge:Number(i),m=a-u;if(!Number.isSafeInteger(u)||!Number.isSafeInteger(a)||m<=0||m>he)throw new Error("stampPrepared: bounded preparedAt/expiresAt required");let P={...l,preparedAt:u,expiresAt:a,prepareVersion:fe},M=xe(P),w=te(n),$=w?Se(M,w):void 0,y={...P,prepareHash:M};return $&&(y.prepareMac=$),y}var ye=/^0x[0-9a-fA-F]{40}$/,be=10000n,E=class extends Error{constructor(t,r){super(t),this.name="TraitSnipePrepareError",this.failureKind=r}};function ne(e={}){let t=e.verdict;if(!t||typeof t!="object")throw new E("prepareTraitSnipeCandidate: verdict object required","verdict-required");if(t.decision!==c.BUY||t.ok!==!0)throw new E(`prepareTraitSnipeCandidate: refusing to prepare a ${t.decision||"unknown"} verdict \u2014 only BUY is preparable`,"not-a-buy");if(!t.edge||!t.liquidity||t.liquidity.illiquid===!0)throw new E("prepareTraitSnipeCandidate: verdict is missing its edge/liquidity evidence","evidence-required");let r=String(e.buyer||"").trim();if(!ye.test(r))throw new E("prepareTraitSnipeCandidate: buyer must be a 0x EVM address \u2014 Oracle never derives it from a key","buyer-required");let n=t.identity||{};if(!n.contract||!n.tokenId)throw new E("prepareTraitSnipeCandidate: verdict identity needs contract and tokenId","identity-required");let o=Number(n.chainId);if(!Number.isInteger(o)||o<=0)throw new E("prepareTraitSnipeCandidate: chainId required","chain-required");let i=q(t.edge.listingPriceWei,"listingPriceWei"),s=BigInt(Math.max(0,Math.trunc(Number(e.maxSlippageBps??0))));if(s>1000n)throw new E("prepareTraitSnipeCandidate: maxSlippageBps is capped at 1000","slippage-cap");let l=i+i*s/be;return re({provider:"nft-trait-sniper",kind:"nft-trait-snipe-intent",traitSniperVersion:j,chainId:o,contract:n.contract,tokenId:n.tokenId,marketplace:n.marketplace,trait:n.trait,buyer:r,intent:{action:"nft-buy-listing",chainId:o,contract:n.contract,tokenId:n.tokenId,marketplace:n.marketplace,listingPriceWei:i.toString(),maxPriceWei:l.toString(),maxSlippageBps:Number(s),buyer:r},transaction:null,calldata:null,decision:t.decision,reasons:t.reasons,liquidity:t.liquidity,edge:t.edge,requiresUserSignature:!0,requiresExternalFillRoute:!0,marketplaceContacted:!1,orderSubmitted:!1,note:"Unsigned intent metadata only. Oracle did not contact a marketplace, build fill calldata, sign, or broadcast. A user-controlled client must source the order and the user must sign it."},{provider:"nft-trait-sniper",kind:"nft-trait-snipe-intent"})}function Ae(e={},t={}){let r=Array.isArray(e.ranked)?e.ranked[0]:null;return r?{candidate:ne({...t,verdict:r}),verdict:r}:{candidate:null,verdict:null}}var Ee=/^0x[0-9a-f]{40}$/,Te=32,Pe=16;function W(e,t){let r=String(e||"").trim().toLowerCase();if(!Ee.test(r)||r==="0x0000000000000000000000000000000000000000")throw new Error(`mint-fanout: valid non-zero ${t} required`);return r}function T(e,t,{allowZero:r=!1}={}){let n=String(e??"").trim();if(!/^(0|[1-9][0-9]*)$/.test(n))throw new Error(`mint-fanout: ${t} must be an unsigned integer`);let o=BigInt(n);if(!r&&o===0n)throw new Error(`mint-fanout: ${t} must be positive`);return o}function Me(e){let t=e>>>0;return()=>{t|=0,t=t+1831565813|0;let r=Math.imul(t^t>>>15,1|t);return r=r+Math.imul(r^r>>>7,61|r)^r,((r^r>>>14)>>>0)/4294967296}}function $e({minMs:e=0,maxMs:t=0,rng:r}){let n=Math.max(0,Number(e)||0),o=Math.max(n,Number(t)||0);return o===0?0:Math.floor(n+r()*(o-n+1))}function Ie(e){return BigInt(e).toString(16).padStart(64,"0")}function ie(e){return e.slice(2).padStart(64,"0")}function oe(e,t,r){return`0x42842e0e${ie(e)}${ie(t)}${Ie(r)}`}function ae(e={}){let t=[];if(!e||typeof e!="object")return{ok:!1,problems:["grant is required"]};let r;try{r=W(e.main,"main")}catch(i){t.push(i.message)}try{W(e.contract,"contract")}catch(i){t.push(i.message)}Number.isInteger(Number(e.chainId))||t.push("grant.chainId is required");let n=Number(e.maxWallets);!Number.isInteger(n)||n<1?t.push("grant.maxWallets must be a positive integer"):n>32&&t.push("grant.maxWallets exceeds hard max 32");try{T(e.maxTotalWei,"grant.maxTotalWei")}catch(i){t.push(i.message)}let o;try{o=W(e.sweepTo,"sweepTo")}catch(i){t.push(i.message)}return r&&o&&o!==r&&t.push("grant.sweepTo must equal grant.main (children may only consolidate back to main)"),{ok:t.length===0,problems:t}}function Ne(e={}){let t=W(e.main,"main"),r=Number(e.chainId);if(!Number.isInteger(r))throw new Error("mint-fanout: chainId required");let n=W(e.contract,"contract"),o=e.grant,i=ae(o);if(!i.ok)throw new Error(`mint-fanout: ${i.problems.join("; ")}`);let s=Array.isArray(e.children)?e.children:[];if(s.length===0)throw new Error("mint-fanout: at least one child wallet required");let l=Math.min(Number(o.maxWallets),32);if(s.length>l)throw new Error(`mint-fanout: ${s.length} children exceeds grant.maxWallets ${l}`);let u=new Set,a=s.map((f,h)=>{let b=Number(f.index);if(!Number.isInteger(b)||b<1)throw new Error(`mint-fanout: child ${h} index must be >= 1 (0 is main)`);let d=W(f.address,`child[${h}]`);if(d===t)throw new Error("mint-fanout: child address must not equal main");if(u.has(d))throw new Error(`mint-fanout: duplicate child ${d}`);return u.add(d),{index:b,address:d}}),m=e.mint||{},x=String(m.data||"").toLowerCase();if(!/^0x[0-9a-f]*$/.test(x)||x.length<10)throw new Error("mint-fanout: mint.data must be hex calldata");let P=T(m.valueWei??m.value??"0","mint.valueWei",{allowZero:!0}),M=T(m.gasLimit,"mint.gasLimit"),w=T(m.maxFeePerGas??m.maxFeePerGasWei,"mint.maxFeePerGas"),$=T(m.maxPriorityFeePerGas??m.maxPriorityFeePerGasWei??"0","mint.maxPriorityFeePerGas",{allowZero:!0}),y=T(e.fundWeiPerChild,"fundWeiPerChild"),G=T(o.maxTotalWei,"grant.maxTotalWei"),R=BigInt(a.length),B=y*R,v=P*R;if(B>G)throw new Error(`mint-fanout: total fund ${B} exceeds grant.maxTotalWei ${G}`);let S=e.sweep||{},D=S.nft!==!1,F=S.native!==!1,k=W(o.sweepTo,"sweepTo"),O=T(S.gasLimit??"150000","sweep.gasLimit"),L=T(S.maxFeePerGas??S.maxFeePerGasWei??m.maxFeePerGas??m.maxFeePerGasWei,"sweep.maxFeePerGas"),V=T(S.maxPriorityFeePerGas??S.maxPriorityFeePerGasWei??$.toString(),"sweep.maxPriorityFeePerGas",{allowZero:!0}),H=Array.isArray(e.tokenIds)?e.tokenIds:[],C=Me(Number(e.rngSeed)>>>0),X={minMs:e.delayMs?.min??0,maxMs:e.delayMs?.max??0,rng:C},I=[],A=f=>{I.push({...f,step:I.length+1,delayMsAfter:$e(X)})};for(let f of a)A({kind:"fund",walletIndex:0,from:t,to:f.address,data:"0x",value:y.toString(),chainId:r,gasLimit:(S.fundGasLimit||"21000").toString(),maxFeePerGas:w.toString(),maxPriorityFeePerGas:$.toString()});for(let f of a)A({kind:"mint",walletIndex:f.index,from:f.address,to:n,data:x,value:P.toString(),chainId:r,gasLimit:M.toString(),maxFeePerGas:w.toString(),maxPriorityFeePerGas:$.toString()});if(D&&a.forEach((f,h)=>{let b=H[h],d=b!=null&&String(b).length>0;A({kind:"sweep-nft",walletIndex:f.index,from:f.address,to:n,data:d?oe(f.address,k,BigInt(b)):"0x",value:"0",chainId:r,gasLimit:O.toString(),maxFeePerGas:L.toString(),maxPriorityFeePerGas:V.toString(),needsTokenId:!d,tokenId:d?String(b):null,nftTo:k})}),F)for(let f of a)A({kind:"sweep-native",walletIndex:f.index,from:f.address,to:k,data:"0x",value:"0",valueMode:"remainder",chainId:r,gasLimit:"21000",maxFeePerGas:L.toString(),maxPriorityFeePerGas:V.toString()});return{execution:"prepare-only",signed:!1,main:t,chainId:r,contract:n,children:a,hopCount:I.length,totals:{wallets:a.length,fundWei:B.toString(),mintValueWei:v.toString()},graph:"fan-out-fan-in (common funder + common sink, linkable)",hops:I}}export{Z as DEFAULT_TRAIT_SNIPE_POLICY,Pe as FANOUT_DEFAULT_MAX,Te as FANOUT_HARD_MAX,c as TRAIT_DECISIONS,j as TRAIT_SNIPER_VERSION,K as TRIGGER_EVENTS,E as TraitSnipePrepareError,oe as erc721SafeTransferCalldata,Q as evaluateTraitListing,Ne as planMintFanout,Ae as prepareTopTraitSnipe,ne as prepareTraitSnipeCandidate,de as scanTraitListings,ae as validateFanoutGrant};
|
package/package.json
CHANGED