{"version":3,"file":"TokenListController.mjs","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":";;;;;;;;;;;;AAKA,OAAO,EAAE,aAAa,EAAE,mCAAmC;AAO3D,OAAO,EAAE,+BAA+B,EAAE,qCAAqC;AAQ/E,OAAO,EACL,8BAA8B,EAC9B,qBAAqB,EACrB,sBAAsB,EACvB,yBAAqB;AACtB,OAAO,EAAgB,uBAAuB,EAAE,4BAAwB;AAExE,0CAA0C;AAC1C,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC5C,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE7C,MAAM,IAAI,GAAG,qBAAqB,CAAC;AAuDnC,MAAM,QAAQ,GAAkC;IAC9C,iBAAiB,EAAE;QACjB,kBAAkB,EAAE,KAAK;QACzB,OAAO,EAAE,KAAK,EAAE,0CAA0C;QAC1D,sBAAsB,EAAE,IAAI;QAC5B,QAAQ,EAAE,IAAI;KACf;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAmB,EAAE;IAC3D,OAAO;QACL,iBAAiB,EAAE,EAAE;KACtB,CAAC;AACJ,CAAC,CAAC;AAOF;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,+BAA+B,EAIvE;IAwDC;;;;;;;;;;OAUG;IACH,YAAY,EACV,OAAO,EACP,oBAAoB,EACpB,QAAQ,GAAG,gBAAgB,EAC3B,qBAAqB,GAAG,iBAAiB,EACzC,SAAS,EACT,KAAK,GAUN;QACC,KAAK,CAAC;YACJ,IAAI;YACJ,QAAQ;YACR,SAAS;YACT,KAAK,EAAE,EAAE,GAAG,wBAAwB,EAAE,EAAE,GAAG,KAAK,EAAE;SACnD,CAAC,CAAC;;QAxFL;;WAEG;QACH,4DAAsD;QAEtD;;WAEG;QACH,yDAAmC;QAEnC;;;WAGG;QACH,8DAAwC;QAExC;;;WAGG;QACM,sDAAoC,IAAI,GAAG,EAAE,EAAC;QAEvD;;WAEG;QACH,yDAAgD,EAAE,EAAC;QAoBnD,kDAA4C;QAEnC,qDAAuB;QAEvB,6DAA+B;QAExC,+CAAc;QAEd,uDAAkC;QAqChC,uBAAA,IAAI,sCAAkB,QAAQ,MAAA,CAAC;QAC/B,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACjC,uBAAA,IAAI,8CAA0B,qBAAqB,MAAA,CAAC;QACpD,uBAAA,IAAI,gCAAY,OAAO,MAAA,CAAC;QACxB,uBAAA,IAAI,wCAAoB,IAAI,eAAe,EAAE,MAAA,CAAC;QAE9C,IAAI,oBAAoB,EAAE,CAAC;YACzB,gFAAgF;YAChF,kEAAkE;YAClE,oBAAoB,CAAC,KAAK,EAAE,sBAAsB,EAAE,EAAE;gBACpD,MAAM,uBAAA,IAAI,2FAAgC,MAApC,IAAI,EAAiC,sBAAsB,CAAC,CAAC;YACrE,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,CAAC,SAAS,CACtB,+BAA+B;YAC/B,gFAAgF;YAChF,kEAAkE;YAClE,KAAK,EAAE,sBAAsB,EAAE,EAAE;gBAC/B,MAAM,uBAAA,IAAI,2FAAgC,MAApC,IAAI,EAAiC,sBAAsB,CAAC,CAAC;YACrE,CAAC,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,uBAAA,IAAI,8CAAmB,EAAE,CAAC;YAC5B,MAAM,uBAAA,IAAI,8CAAmB,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,MAAM,WAAW,GAAG,KAAK,IAAmB,EAAE;YAC5C,IAAI,CAAC;gBACH,MAAM,uBAAA,IAAI,wFAA6B,MAAjC,IAAI,CAA+B,CAAC;gBAE1C,wEAAwE;gBACxE,IAAI,CAAC,SAAS,CAAC,SAAS,CACtB,iCAAiC,EACjC,CAAC,QAA2B,EAAE,EAAE,CAAC,uBAAA,IAAI,2EAAgB,MAApB,IAAI,EAAiB,QAAQ,CAAC,EAC/D,CAAC,eAAe,EAAE,EAAE,CAAC,eAAe,CAAC,iBAAiB,CACvD,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,aAAa;YACf,CAAC;oBAAS,CAAC;gBACT,uBAAA,IAAI,0CAAsB,SAAS,MAAA,CAAC;YACtC,CAAC;QACH,CAAC,CAAC;QAEF,uBAAA,IAAI,0CAAsB,WAAW,EAAE,MAAA,CAAC;QAExC,MAAM,uBAAA,IAAI,8CAAmB,CAAC;IAChC,CAAC;IAqPD,4FAA4F;IAC5F,qGAAqG;IACrG;;;;;OAKG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,8BAA8B,CAAC,uBAAA,IAAI,oCAAS,CAAC,EAAE,CAAC;YACnD,OAAO;QACT,CAAC;QACD,MAAM,uBAAA,IAAI,mFAAwB,MAA5B,IAAI,CAA0B,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO;QACX,uBAAA,IAAI,wEAAa,MAAjB,IAAI,CAAe,CAAC;QACpB,MAAM,uBAAA,IAAI,mFAAwB,MAA5B,IAAI,CAA0B,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,IAAI;QACF,uBAAA,IAAI,wEAAa,MAAjB,IAAI,CAAe,CAAC;IACtB,CAAC;IAED;;;;;OAKG;IACM,OAAO;QACd,KAAK,CAAC,OAAO,EAAE,CAAC;QAChB,uBAAA,IAAI,wEAAa,MAAjB,IAAI,CAAe,CAAC;QAEpB,sDAAsD;QACtD,IAAI,uBAAA,IAAI,iDAAsB,EAAE,CAAC;YAC/B,YAAY,CAAC,uBAAA,IAAI,iDAAsB,CAAC,CAAC;YACzC,uBAAA,IAAI,6CAAyB,SAAS,MAAA,CAAC;QACzC,CAAC;QACD,uBAAA,IAAI,mDAAwB,CAAC,KAAK,EAAE,CAAC;IACvC,CAAC;IA8BD;;;;;;OAMG;IACH,KAAK,CAAC,YAAY,CAAC,EAAE,OAAO,EAAyB;QACnD,MAAM,uBAAA,IAAI,kFAAuB,MAA3B,IAAI,CAAyB,CAAC;QACpC,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,cAAc,CAAC,OAAY;QAC/B,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/B,OAAO;QACT,CAAC;QAED,sCAAsC;QACtC,MAAM,aAAa,GAAG,MAAM,aAAa,CACvC,GAAG,EAAE,CACH,uBAAuB,CACrB,OAAO,EACP,uBAAA,IAAI,4CAAiB,CAAC,MAAM,CACA,CACjC,CAAC;QAEF,0CAA0C;QAC1C,IAAI,aAAa,EAAE,CAAC;YAClB,qDAAqD;YACrD,MAAM,SAAS,GAAiB,EAAE,CAAC;YACnC,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;gBAClC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG;oBACzB,GAAG,KAAK;oBACR,WAAW,EAAE,qBAAqB,CAAC,KAAK,CAAC,WAAW,CAAC;oBACrD,OAAO,EAAE,sBAAsB,CAAC;wBAC9B,OAAO;wBACP,YAAY,EAAE,KAAK,CAAC,OAAO;qBAC5B,CAAC;iBACH,CAAC;YACJ,CAAC;YAED,oEAAoE;YACpE,MAAM,YAAY,GAAc;gBAC9B,IAAI,EAAE,SAAS;gBACf,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC;YAClD,CAAC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,iEAAiE;QACjE,qEAAqE;QACrE,0EAA0E;QAC1E,2EAA2E;QAC3E,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YAC5D,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,2EAA2E;gBAC3E,MAAM,YAAY,GAAc,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;gBACpE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;oBACpB,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC;gBAClD,CAAC,CAAC,CAAC;YACL,CAAC;YACD,+EAA+E;QACjF,CAAC;IACH,CAAC;IAED,YAAY,CAAC,OAAY;QACvB,MAAM,EAAE,iBAAiB,EAAE,GAAmB,IAAI,CAAC,KAAK,CAAC;QACzD,MAAM,SAAS,GAAuB,iBAAiB,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC;QAC5E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,OAAO,CACL,SAAS,KAAK,SAAS,IAAI,GAAG,GAAG,SAAS,GAAG,uBAAA,IAAI,kDAAuB,CACzE,CAAC;IACJ,CAAC;;utBAlgB0B,OAAY;IACrC,OAAO,GAAG,uBAAA,EAAmB,iDAAkB,IAAI,OAAO,EAAE,CAAC;AAC/D,CAAC;AAyGD;;;GAGG;AACH,KAAK;IACH,IAAI,CAAC;QACH,MAAM,uBAAA,IAAI,8CAAmB,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,aAAa;IACf,CAAC;AACH,CAAC,qFAQe,QAA2B;IACzC,+DAA+D;IAC/D,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAU,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,uBAAA,IAAI,sDAA2B,CAAC,OAAO,CAAC,CAAC;QAE1D,6DAA6D;QAC7D,IAAI,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC;YAC9C,uBAAA,IAAI,mDAAwB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,kCAAkC;IAClC,uBAAA,IAAI,kDAA8B,EAAE,GAAG,QAAQ,EAAE,MAAA,CAAC;IAElD,4CAA4C;IAC5C,IAAI,uBAAA,IAAI,mDAAwB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QAC1C,uBAAA,IAAI,4EAAiB,MAArB,IAAI,CAAmB,CAAC;IAC1B,CAAC;AACH,CAAC;IAMC,IAAI,uBAAA,IAAI,iDAAsB,EAAE,CAAC;QAC/B,YAAY,CAAC,uBAAA,IAAI,iDAAsB,CAAC,CAAC;IAC3C,CAAC;IAED,uBAAA,IAAI,6CAAyB,UAAU,CAAC,GAAG,EAAE;QAC3C,sFAAsF;QACtF,mCAAmC;QACnC,mEAAmE;QACnE,uBAAA,IAAI,iFAAsB,MAA1B,IAAI,CAAwB,CAAC;IAC/B,CAAC,EAAE,uBAAA,EAAmB,kDAAmB,CAAC,MAAA,CAAC;AAC7C,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK;IACH,IAAI,uBAAA,IAAI,mDAAwB,EAAE,CAAC;QACjC,qFAAqF;QACrF,IAAI,uBAAA,IAAI,mDAAwB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC1C,uBAAA,IAAI,4EAAiB,MAArB,IAAI,CAAmB,CAAC;QAC1B,CAAC;QACD,OAAO;IACT,CAAC;IAED,MAAM,eAAe,GAAG,CAAC,GAAG,uBAAA,IAAI,mDAAwB,CAAC,CAAC;IAC1D,uBAAA,IAAI,mDAAwB,CAAC,KAAK,EAAE,CAAC;IAErC,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO;IACT,CAAC;IAED,uBAAA,IAAI,+CAA2B,OAAO,CAAC,GAAG,CACxC,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAA,IAAI,oFAAyB,MAA7B,IAAI,EAA0B,OAAO,CAAC,CAAC,CACzE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,MAAA,CAAC;IAExB,IAAI,CAAC;QACH,MAAM,uBAAA,IAAI,mDAAwB,CAAC;IACrC,CAAC;YAAS,CAAC;QACT,uBAAA,IAAI,+CAA2B,SAAS,MAAA,CAAC;IAC3C,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK;IACH,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACvC,2BAA2B,EAC3B,IAAI,CACL,CAAC;QAEF,iEAAiE;QACjE,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACvC,GAAG,CAAC,UAAU,CAAC,GAAG,uBAAA,EAAmB,iDAAkB,GAAG,CAAC,CAC5D,CAAC;QAEF,8BAA8B;QAC9B,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CACnC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC1B,4DAA4D;YAC5D,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAQ,CAAC;YAEzC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACjD,wBAAwB,EACxB,IAAI,EACJ,GAAG,CACJ,CAAC;YAEF,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,KAAK,CACX,gDAAgD,OAAO,GAAG,EAC1D,KAAK,CACN,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAmB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAChE,CAAC,CAAC,CACH,CAAC;QAEF,0CAA0C;QAC1C,MAAM,WAAW,GAAsB,EAAE,CAAC;QAC1C,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;YACjC,IAAI,UAAU,EAAE,CAAC;gBACf,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC;YACpD,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,yEAAyE;QACzE,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAU,CACnD,CAAC;QAEF,mEAAmE;QACnE,0DAA0D;QAC1D,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,qDAAqD;gBACrD,kEAAkE;gBAClE,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC/D,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAc,CAAC,EAAE,CAAC;wBAC7C,KAAK,CAAC,iBAAiB,CAAC,OAAc,CAAC,GAAG,SAAS,CAAC;oBACtD,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAED,uEAAuE;QACvE,6EAA6E;QAC7E,yEAAyE;QACzE,yCAAyC;QACzC,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;YACpC,uBAAA,IAAI,mDAAwB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QAED,2CAA2C;QAC3C,IAAI,uBAAA,IAAI,mDAAwB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC1C,uBAAA,IAAI,4EAAiB,MAArB,IAAI,CAAmB,CAAC;QAC1B,CAAC;QAED,uBAAA,IAAI,kDAA8B,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,MAAA,CAAC;IACxE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,yDAAyD,EACzD,KAAK,CACN,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,KAAK,uDAA0B,OAAY;IACzC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAExD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,gDAAgD,OAAO,EAAE,CAAC,CAAC;YACxE,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAG,uBAAA,EAAmB,mDAAoB,MAAvC,EAAmB,EAAqB,OAAO,CAAC,CAAC;QAEpE,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACvB,wBAAwB,EACxB,IAAI,EACJ,UAAU,EACV,SAAS,CACV,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,iDAAiD,OAAO,GAAG,EAC3D,KAAK,CACN,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,8DACH,sBAAoC;IAEpC,MAAM,qBAAqB,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAC/C,wCAAwC,EACxC,sBAAsB,CAAC,uBAAuB,CAC/C,CAAC;IACF,MAAM,EAAE,OAAO,EAAE,GAAG,qBAAqB,CAAC,aAAa,CAAC;IAExD,IAAI,uBAAA,IAAI,oCAAS,KAAK,OAAO,EAAE,CAAC;QAC9B,uBAAA,IAAI,4CAAiB,CAAC,KAAK,EAAE,CAAC;QAC9B,uBAAA,IAAI,wCAAoB,IAAI,eAAe,EAAE,MAAA,CAAC;QAC9C,uBAAA,IAAI,gCAAY,OAAO,MAAA,CAAC;IAC1B,CAAC;AACH,CAAC;IA+DC,IAAI,uBAAA,IAAI,uCAAY,EAAE,CAAC;QACrB,aAAa,CAAC,uBAAA,IAAI,uCAAY,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK;IACH,mDAAmD;IACnD,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,uBAAA,IAAI,oCAAS,CAAC,CAAC,CAAC;IAC9D,gFAAgF;IAChF,kEAAkE;IAClE,uBAAA,IAAI,mCAAe,WAAW,CAAC,KAAK,IAAI,EAAE;QACxC,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,uBAAA,IAAI,oCAAS,CAAC,CAAC,CAAC;IAChE,CAAC,EAAE,uBAAA,IAAI,0CAAe,CAAC,MAAA,CAAC;AAC1B,CAAC;AA5bD;;GAEG;AACa,kDAAqB,GAAG,EAAN,CAAO;AAEzC,yCAAyC;AACzB,iDAAoB,mBAAmB,EAAtB,CAAuB;AA6gB1D,eAAe,mBAAmB,CAAC","sourcesContent":["import type {\n  ControllerGetStateAction,\n  ControllerStateChangeEvent,\n  StateMetadata,\n} from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n  NetworkControllerStateChangeEvent,\n  NetworkState,\n  NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n  StorageServiceSetItemAction,\n  StorageServiceGetItemAction,\n  StorageServiceGetAllKeysAction,\n} from '@metamask/storage-service';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n  isTokenListSupportedForNetwork,\n  formatAggregatorNames,\n  formatIconUrlWithProxy,\n} from './assetsUtil';\nimport { TokenRwaData, fetchTokenListByChainId } from './token-service';\n\n// 4 Hour Interval Cache Refresh Threshold\nconst DEFAULT_INTERVAL = 4 * 60 * 60 * 1000;\nconst DEFAULT_THRESHOLD = 4 * 60 * 60 * 1000;\n\nconst name = 'TokenListController';\n\nexport type TokenListToken = {\n  name: string;\n  symbol: string;\n  decimals: number;\n  address: string;\n  occurrences: number;\n  aggregators: string[];\n  iconUrl: string;\n  rwaData?: TokenRwaData;\n};\n\nexport type TokenListMap = Record<string, TokenListToken>;\n\nexport type DataCache = {\n  timestamp: number;\n  data: TokenListMap;\n};\nexport type TokensChainsCache = {\n  [chainId: Hex]: DataCache;\n};\n\nexport type TokenListState = {\n  tokensChainsCache: TokensChainsCache;\n};\n\nexport type TokenListStateChange = ControllerStateChangeEvent<\n  typeof name,\n  TokenListState\n>;\n\nexport type TokenListControllerEvents = TokenListStateChange;\n\nexport type GetTokenListState = ControllerGetStateAction<\n  typeof name,\n  TokenListState\n>;\n\nexport type TokenListControllerActions = GetTokenListState;\n\ntype AllowedActions =\n  | NetworkControllerGetNetworkClientByIdAction\n  | StorageServiceSetItemAction\n  | StorageServiceGetItemAction\n  | StorageServiceGetAllKeysAction;\n\ntype AllowedEvents = NetworkControllerStateChangeEvent;\n\nexport type TokenListControllerMessenger = Messenger<\n  typeof name,\n  TokenListControllerActions | AllowedActions,\n  TokenListControllerEvents | AllowedEvents\n>;\n\nconst metadata: StateMetadata<TokenListState> = {\n  tokensChainsCache: {\n    includeInStateLogs: false,\n    persist: false, // Persisted separately via StorageService\n    includeInDebugSnapshot: true,\n    usedInUi: true,\n  },\n};\n\nexport const getDefaultTokenListState = (): TokenListState => {\n  return {\n    tokensChainsCache: {},\n  };\n};\n\n/** The input to start polling for the {@link TokenListController} */\ntype TokenListPollingInput = {\n  chainId: Hex;\n};\n\n/**\n * Controller that passively polls on a set interval for the list of tokens from metaswaps api\n */\nexport class TokenListController extends StaticIntervalPollingController<TokenListPollingInput>()<\n  typeof name,\n  TokenListState,\n  TokenListControllerMessenger\n> {\n  /**\n   * Debounce timer for persisting state changes to storage.\n   */\n  #persistDebounceTimer?: ReturnType<typeof setTimeout>;\n\n  /**\n   * Promise for the in-flight initialization sequence.\n   */\n  #initializePromise?: Promise<void>;\n\n  /**\n   * Promise that resolves when the current persist operation completes.\n   * Used to prevent race conditions between persist operations.\n   */\n  #persistInFlightPromise?: Promise<void>;\n\n  /**\n   * Tracks which chains have pending changes to persist.\n   * Only changed chains are persisted to reduce write amplification.\n   */\n  readonly #changedChainsToPersist: Set<Hex> = new Set();\n\n  /**\n   * Previous tokensChainsCache for detecting which chains changed.\n   */\n  #previousTokensChainsCache: TokensChainsCache = {};\n\n  /**\n   * Debounce delay for persisting state changes (in milliseconds).\n   */\n  static readonly #persistDebounceMs = 500;\n\n  // Storage key prefix for per-chain files\n  static readonly #storageKeyPrefix = 'tokensChainsCache';\n\n  /**\n   * Get storage key for a specific chain.\n   *\n   * @param chainId - The chain ID.\n   * @returns Storage key for the chain.\n   */\n  static #getChainStorageKey(chainId: Hex): string {\n    return `${TokenListController.#storageKeyPrefix}:${chainId}`;\n  }\n\n  #intervalId?: ReturnType<typeof setTimeout>;\n\n  readonly #intervalDelay: number;\n\n  readonly #cacheRefreshThreshold: number;\n\n  #chainId: Hex;\n\n  #abortController: AbortController;\n\n  /**\n   * Creates a TokenListController instance.\n   *\n   * @param options - The controller options.\n   * @param options.chainId - The chain ID of the current network.\n   * @param options.onNetworkStateChange - A function for registering an event handler for network state changes.\n   * @param options.interval - The polling interval, in milliseconds.\n   * @param options.cacheRefreshThreshold - The token cache expiry time, in milliseconds.\n   * @param options.messenger - A restricted messenger.\n   * @param options.state - Initial state to set on this controller.\n   */\n  constructor({\n    chainId,\n    onNetworkStateChange,\n    interval = DEFAULT_INTERVAL,\n    cacheRefreshThreshold = DEFAULT_THRESHOLD,\n    messenger,\n    state,\n  }: {\n    chainId: Hex;\n    onNetworkStateChange?: (\n      listener: (networkState: NetworkState) => void,\n    ) => void;\n    interval?: number;\n    cacheRefreshThreshold?: number;\n    messenger: TokenListControllerMessenger;\n    state?: Partial<TokenListState>;\n  }) {\n    super({\n      name,\n      metadata,\n      messenger,\n      state: { ...getDefaultTokenListState(), ...state },\n    });\n\n    this.#intervalDelay = interval;\n    this.setIntervalLength(interval);\n    this.#cacheRefreshThreshold = cacheRefreshThreshold;\n    this.#chainId = chainId;\n    this.#abortController = new AbortController();\n\n    if (onNetworkStateChange) {\n      // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n      // eslint-disable-next-line @typescript-eslint/no-misused-promises\n      onNetworkStateChange(async (networkControllerState) => {\n        await this.#onNetworkControllerStateChange(networkControllerState);\n      });\n    } else {\n      this.messenger.subscribe(\n        'NetworkController:stateChange',\n        // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n        // eslint-disable-next-line @typescript-eslint/no-misused-promises\n        async (networkControllerState) => {\n          await this.#onNetworkControllerStateChange(networkControllerState);\n        },\n      );\n    }\n  }\n\n  /**\n   * Initialize the controller by loading cache from storage and running migration.\n   * This method should be called by clients after construction.\n   *\n   * @returns A promise that resolves when initialization is complete.\n   */\n  async initialize(): Promise<void> {\n    if (this.#initializePromise) {\n      await this.#initializePromise;\n      return;\n    }\n\n    const executeInit = async (): Promise<void> => {\n      try {\n        await this.#synchronizeCacheWithStorage();\n\n        // Subscribe to state changes to automatically persist tokensChainsCache\n        this.messenger.subscribe(\n          'TokenListController:stateChange',\n          (newCache: TokensChainsCache) => this.#onCacheChanged(newCache),\n          (controllerState) => controllerState.tokensChainsCache,\n        );\n      } catch {\n        // do nothing\n      } finally {\n        this.#initializePromise = undefined;\n      }\n    };\n\n    this.#initializePromise = executeInit();\n\n    await this.#initializePromise;\n  }\n\n  /**\n   * Waits for any in-flight initialization to complete.\n   * Polling should not run against partially initialized state.\n   */\n  async #waitForInitialization(): Promise<void> {\n    try {\n      await this.#initializePromise;\n    } catch {\n      // do nothing\n    }\n  }\n\n  /**\n   * Handle tokensChainsCache changes by detecting which chains changed\n   * and scheduling debounced persistence.\n   *\n   * @param newCache - The new tokensChainsCache state.\n   */\n  #onCacheChanged(newCache: TokensChainsCache): void {\n    // Detect which chains changed by comparing with previous cache\n    for (const chainId of Object.keys(newCache) as Hex[]) {\n      const newData = newCache[chainId];\n      const prevData = this.#previousTokensChainsCache[chainId];\n\n      // Chain is new or timestamp changed (indicating data update)\n      if (prevData?.timestamp !== newData.timestamp) {\n        this.#changedChainsToPersist.add(chainId);\n      }\n    }\n\n    // Update previous cache reference\n    this.#previousTokensChainsCache = { ...newCache };\n\n    // Schedule persistence if there are changes\n    if (this.#changedChainsToPersist.size > 0) {\n      this.#debouncePersist();\n    }\n  }\n\n  /**\n   * Debounce persistence of changed chains to storage.\n   */\n  #debouncePersist(): void {\n    if (this.#persistDebounceTimer) {\n      clearTimeout(this.#persistDebounceTimer);\n    }\n\n    this.#persistDebounceTimer = setTimeout(() => {\n      // Note: #persistChangedChains handles errors internally via #saveChainCacheToStorage,\n      // so this promise will not reject.\n      // eslint-disable-next-line @typescript-eslint/no-floating-promises\n      this.#persistChangedChains();\n    }, TokenListController.#persistDebounceMs);\n  }\n\n  /**\n   * Persist only the chains that have changed to storage.\n   * Reduces write amplification by skipping unchanged chains.\n   *\n   * If a persist operation is already in-flight, this method returns early\n   * and reschedules the debounce to ensure accumulated changes are retried\n   * after the current operation completes.\n   *\n   * @returns A promise that resolves when changed chains are persisted.\n   */\n  async #persistChangedChains(): Promise<void> {\n    if (this.#persistInFlightPromise) {\n      // Reschedule debounce to retry accumulated changes after in-flight persist completes\n      if (this.#changedChainsToPersist.size > 0) {\n        this.#debouncePersist();\n      }\n      return;\n    }\n\n    const chainsToPersist = [...this.#changedChainsToPersist];\n    this.#changedChainsToPersist.clear();\n\n    if (chainsToPersist.length === 0) {\n      return;\n    }\n\n    this.#persistInFlightPromise = Promise.all(\n      chainsToPersist.map((chainId) => this.#saveChainCacheToStorage(chainId)),\n    ).then(() => undefined);\n\n    try {\n      await this.#persistInFlightPromise;\n    } finally {\n      this.#persistInFlightPromise = undefined;\n    }\n  }\n\n  /**\n   * Synchronize tokensChainsCache between state and storage bidirectionally.\n   *\n   * This method:\n   * 1. Loads cached chains from storage (per-chain files) in parallel\n   * 2. Merges loaded data into state (preferring existing state to avoid overwriting fresh data)\n   * 3. Persists any chains that exist in state but not in storage\n   *\n   * Called during initialization to ensure state and storage are consistent.\n   *\n   * @returns A promise that resolves when synchronization is complete.\n   */\n  async #synchronizeCacheWithStorage(): Promise<void> {\n    try {\n      const allKeys = await this.messenger.call(\n        'StorageService:getAllKeys',\n        name,\n      );\n\n      // Filter keys that belong to tokensChainsCache (per-chain files)\n      const cacheKeys = allKeys.filter((key) =>\n        key.startsWith(`${TokenListController.#storageKeyPrefix}:`),\n      );\n\n      // Load all chains in parallel\n      const chainCaches = await Promise.all(\n        cacheKeys.map(async (key) => {\n          // Extract chainId from key: 'tokensChainsCache:0x1' → '0x1'\n          const chainId = key.split(':')[1] as Hex;\n\n          const { result, error } = await this.messenger.call(\n            'StorageService:getItem',\n            name,\n            key,\n          );\n\n          if (error) {\n            console.error(\n              `TokenListController: Error loading cache for ${chainId}:`,\n              error,\n            );\n            return null;\n          }\n\n          return result ? { chainId, data: result as DataCache } : null;\n        }),\n      );\n\n      // Build complete cache from loaded chains\n      const loadedCache: TokensChainsCache = {};\n      chainCaches.forEach((chainCache) => {\n        if (chainCache) {\n          loadedCache[chainCache.chainId] = chainCache.data;\n        }\n      });\n\n      // Chains in state _before loading persisted state_, from a recent update\n      const chainsInState = new Set(\n        Object.keys(this.state.tokensChainsCache) as Hex[],\n      );\n\n      // Merge loaded cache with existing state, preferring existing data\n      // (which may be fresher if fetched during initialization)\n      if (Object.keys(loadedCache).length > 0) {\n        this.update((state) => {\n          // Only load chains that don't already exist in state\n          // This prevents overwriting fresh API data with stale cached data\n          for (const [chainId, cacheData] of Object.entries(loadedCache)) {\n            if (!state.tokensChainsCache[chainId as Hex]) {\n              state.tokensChainsCache[chainId as Hex] = cacheData;\n            }\n          }\n        });\n      }\n\n      // Persist chains that exist in state but were not loaded from storage.\n      // This handles the case where initial state contains chains that don't exist\n      // in storage yet (e.g., fresh data from API). Without this, those chains\n      // would be lost on the next app restart.\n      for (const chainId of chainsInState) {\n        this.#changedChainsToPersist.add(chainId);\n      }\n\n      // Persist any chains that need to be saved\n      if (this.#changedChainsToPersist.size > 0) {\n        this.#debouncePersist();\n      }\n\n      this.#previousTokensChainsCache = { ...this.state.tokensChainsCache };\n    } catch (error) {\n      console.error(\n        'TokenListController: Failed to load cache from storage:',\n        error,\n      );\n    }\n  }\n\n  /**\n   * Save a specific chain's cache to StorageService.\n   * This persists only the updated chain's data, reducing write amplification.\n   *\n   * @param chainId - The chain ID to save.\n   * @returns A promise that resolves when saving is complete.\n   */\n  async #saveChainCacheToStorage(chainId: Hex): Promise<void> {\n    try {\n      const chainData = this.state.tokensChainsCache[chainId];\n\n      if (!chainData) {\n        console.warn(`TokenListController: No cache data for chain ${chainId}`);\n        return;\n      }\n\n      const storageKey = TokenListController.#getChainStorageKey(chainId);\n\n      await this.messenger.call(\n        'StorageService:setItem',\n        name,\n        storageKey,\n        chainData,\n      );\n    } catch (error) {\n      console.error(\n        `TokenListController: Failed to save cache for ${chainId}:`,\n        error,\n      );\n    }\n  }\n\n  /**\n   * Updates state and restarts polling on changes to the network controller\n   * state.\n   *\n   * @param networkControllerState - The updated network controller state.\n   */\n  async #onNetworkControllerStateChange(\n    networkControllerState: NetworkState,\n  ): Promise<void> {\n    const selectedNetworkClient = this.messenger.call(\n      'NetworkController:getNetworkClientById',\n      networkControllerState.selectedNetworkClientId,\n    );\n    const { chainId } = selectedNetworkClient.configuration;\n\n    if (this.#chainId !== chainId) {\n      this.#abortController.abort();\n      this.#abortController = new AbortController();\n      this.#chainId = chainId;\n    }\n  }\n\n  // Eventually we want to remove start/restart/stop controls in favor of new _executePoll API\n  // Maintaining these functions for now until we can safely deprecate them for backwards compatibility\n  /**\n   * Start polling for the token list.\n   *\n   * @deprecated This method is deprecated and will be removed in the future.\n   * Consider using the new polling approach instead\n   */\n  async start(): Promise<void> {\n    if (!isTokenListSupportedForNetwork(this.#chainId)) {\n      return;\n    }\n    await this.#startDeprecatedPolling();\n  }\n\n  /**\n   * Restart polling for the token list.\n   *\n   * @deprecated This method is deprecated and will be removed in the future.\n   * Consider using the new polling approach instead\n   */\n  async restart(): Promise<void> {\n    this.#stopPolling();\n    await this.#startDeprecatedPolling();\n  }\n\n  /**\n   * Stop polling for the token list.\n   *\n   * @deprecated This method is deprecated and will be removed in the future.\n   * Consider using the new polling approach instead\n   */\n  stop(): void {\n    this.#stopPolling();\n  }\n\n  /**\n   * This stops any active polling.\n   *\n   * @deprecated This method is deprecated and will be removed in the future.\n   * Consider using the new polling approach instead\n   */\n  override destroy(): void {\n    super.destroy();\n    this.#stopPolling();\n\n    // Cancel any pending debounced persistence operations\n    if (this.#persistDebounceTimer) {\n      clearTimeout(this.#persistDebounceTimer);\n      this.#persistDebounceTimer = undefined;\n    }\n    this.#changedChainsToPersist.clear();\n  }\n\n  /**\n   * This stops any active polling intervals.\n   *\n   * @deprecated This method is deprecated and will be removed in the future.\n   * Consider using the new polling approach instead\n   */\n  #stopPolling(): void {\n    if (this.#intervalId) {\n      clearInterval(this.#intervalId);\n    }\n  }\n\n  /**\n   * Starts a new polling interval for a given chainId (this should be deprecated in favor of _executePoll)\n   *\n   * @deprecated This method is deprecated and will be removed in the future.\n   * Consider using the new polling approach instead\n   */\n  async #startDeprecatedPolling(): Promise<void> {\n    // renaming this to avoid collision with base class\n    await safelyExecute(() => this.fetchTokenList(this.#chainId));\n    // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n    // eslint-disable-next-line @typescript-eslint/no-misused-promises\n    this.#intervalId = setInterval(async () => {\n      await safelyExecute(() => this.fetchTokenList(this.#chainId));\n    }, this.#intervalDelay);\n  }\n\n  /**\n   * This starts a new polling loop for any given chain. Under the hood it is deduping polls\n   *\n   * @param input - The input for the poll.\n   * @param input.chainId - The chainId of the chain to trigger the fetch.\n   * @returns A promise that resolves when this operation completes.\n   */\n  async _executePoll({ chainId }: TokenListPollingInput): Promise<void> {\n    await this.#waitForInitialization();\n    return this.fetchTokenList(chainId);\n  }\n\n  /**\n   * Fetching token list from the Token Service API. This will fetch tokens across chains.\n   * State changes are automatically persisted via the stateChange subscription.\n   *\n   * @param chainId - The chainId of the current chain triggering the fetch.\n   */\n  async fetchTokenList(chainId: Hex): Promise<void> {\n    if (this.isCacheValid(chainId)) {\n      return;\n    }\n\n    // Fetch fresh token list from the API\n    const tokensFromAPI = await safelyExecute(\n      () =>\n        fetchTokenListByChainId(\n          chainId,\n          this.#abortController.signal,\n        ) as Promise<TokenListToken[]>,\n    );\n\n    // Have response - process and update list\n    if (tokensFromAPI) {\n      // Format tokens from API (HTTP) and update tokenList\n      const tokenList: TokenListMap = {};\n      for (const token of tokensFromAPI) {\n        tokenList[token.address] = {\n          ...token,\n          aggregators: formatAggregatorNames(token.aggregators),\n          iconUrl: formatIconUrlWithProxy({\n            chainId,\n            tokenAddress: token.address,\n          }),\n        };\n      }\n\n      // Update state - persistence happens automatically via subscription\n      const newDataCache: DataCache = {\n        data: tokenList,\n        timestamp: Date.now(),\n      };\n      this.update((state) => {\n        state.tokensChainsCache[chainId] = newDataCache;\n      });\n      return;\n    }\n\n    // No response - fallback to previous state, or initialise empty.\n    // Only initialize with a new timestamp if there's no existing cache.\n    // If there's existing cache, keep it as-is without updating the timestamp\n    // to avoid making stale data appear \"fresh\" and preventing retry attempts.\n    if (!tokensFromAPI) {\n      const existingCache = this.state.tokensChainsCache[chainId];\n      if (!existingCache) {\n        // No existing cache - initialize empty (persistence happens automatically)\n        const newDataCache: DataCache = { data: {}, timestamp: Date.now() };\n        this.update((state) => {\n          state.tokensChainsCache[chainId] = newDataCache;\n        });\n      }\n      // If there's existing cache, keep it as-is (don't update timestamp or persist)\n    }\n  }\n\n  isCacheValid(chainId: Hex): boolean {\n    const { tokensChainsCache }: TokenListState = this.state;\n    const timestamp: number | undefined = tokensChainsCache[chainId]?.timestamp;\n    const now = Date.now();\n    return (\n      timestamp !== undefined && now - timestamp < this.#cacheRefreshThreshold\n    );\n  }\n}\n\nexport default TokenListController;\n"]}